MYMAKE(1)

πŸ‘‘ MYMAKE(1) β€” the gen-z bestie guide to mymake πŸ’…

okay so. you opened the manual page for mymake, which lives in the General Commands Manual section (section 1, bestie, we're in commands territory). sit down, hydrate, and let's get into it ✨


🌸 NAME 🌸

mymake β€” simple build system for C/C++ 🫢


✨ SYNOPSIS ✨

like, here's the whole vibe in one breath:

mymake [-?] [-f] [-c] [-t] [-e] [-ne] [-p path] [-j processes] [-o
output] [-d level] [--default-input input] [files]... [options]... [--
args...]
mymake --config
mymake --target
mymake --project

three whole modes up in there, plus a big ol' pile of flags. we love a versatile icon πŸ’–


πŸ¦‹ DESCRIPTION πŸ¦‹

so the mymake program builds C/C++ programs. that's the whole job. it's aiming to build simple programs (we call those targets) with close to zero configuration, but still be powerful enough to handle the bigger programs once your lil baby project grows up. main character energy from day one, fr fr πŸŒ±β†’πŸŒ³

once you've installed it, run:

mymake --config

and that generates the global configuration file for the current user. this file is basically the blueprint: it specifies how to compile C/C++ programs on the current system, and it defines the default parameters that you can use in your other files (more on that below). ngl, the default contents are typically sufficient β€” you probably don't need to touch it πŸ’…

after that initial configuration, compiling a simple program is literally: navigate to the directory with the source code of the program, and type

mymake myprogram.cpp

that compiles and links the program using the configuration in the global configuration, and β€” if compilation is successful β€” it also executes the program. build and run, one line, we ate πŸ”₯

you can pass command-line parameters to the program using the -- or the -a parameter:

mymake myprogram.cpp -- param1 param2...

and the default behavior also works with programs consisting of multiple files, which is iconic. mymake examines which files are included in the source file you specified on the command-line, and compiles those files as well. this is based on the assumption that if the file utils.h is included, then the file utils.cpp should also be compiled (if it exists). of course, different extensions than .cpp are also examined. and as is typically the case with other build systems, only files changed since the last build are rebuilt. no wasted work, periodt πŸŽ€

now β€” if the program being compiled does not follow the assumption that header files and implementation files appear pairwise, it's possible to create a file called .mymake in the project directory and add the following to it:

[]
input=*

this instructs mymake to include all implementation files in the compilation, regardless of whether or not they are considered to be needed by the program or not. it's also possible to add a particular file to the input statement (e.g. myprogram.cpp). in both cases, it will no longer be necessary to specify a file on the command line. so chill of it honestly πŸ™ˆ

as of version 2.3.0, mymake tracks the command-lines used to compile each file, and re-compiles source files where the command line has changed. this means mymake is able to re-compile the relevant parts of the project if the configuration file has changed. it remembers, bestie 🧠✨

and as the project grows, it's also possible to divide the project into separate sub-projects and have mymake manage dependencies automatically. this is done by creating a .myproject file in the root directory of the project. this tells mymake to treat all directories as sub-projects, each which may contain their own .mymake file. see CONFIGURATION for details πŸ‘€


πŸ’– OPTIONS πŸ’–

the usual GNU command line syntax is respected, with long options starting with two dashes (`-'). long options are equivalent to short options. (yes the original really does write the two-dash thing like that, we keep the facts exactly okay 🫢)

  • -?, --help β€” print a message outlining basic usage and command-line options. your lil "help me bestie" button πŸ†˜
  • -f, --force β€” force re-compilation of all required files, even files that would otherwise be considered to be up-to-date. this is useful if the configuration files were changed, for example. it's giving "no, do it AGAIN" πŸ’…
  • -c, --clean β€” clean build files by removing the execDir and buildDir indicated by the configuration based on any options specified. bye bye build artifacts, it's a cleanse ✨
  • -t, --time β€” output information about the wall-clock time used in each step of compilation. clock the clock ⏱️
  • -e, --execute β€” execute the executable after successfully building a target. the default configuration executes the program, but this behavior may be overridden by project-specific configuration files. the command-line parameters overrides all configuration files. πŸ”₯
  • -ne, --not --execute β€” do not execute the executable after a successful build. the inverse of -f. (yep, the manual says -f there. we are relaying, not fixing 🫑 β€” the facts are the facts ngl)
  • -p, --exec-path β€” specify the working directory when running the compiled executable. the default value is the path of the project to which the executable belongs. 🏠
  • -j processes, --threads processes β€” spawn up to the specified number of processes in parallel during compilation. this typically means that compilation may use up to the specified number of hardware threads. this is typically not needed, as the user's preference is specified in the global configuration file. go go go, parallel princess πŸ‘‘
  • -C directory, --directory directory β€” change the current directory to directory before doing anything. this is convenient if one desires to build (and run) a program in a different directory without changing there first. equivalent to the -C flag in make, or the following expression that starts a subshell in Bash: (cd <directory>; mymake <args>) 🐚
  • -o output, --output output β€” specify the name of the output file (note: not the location). this can be used to override the default behavior, but this is better done in one of the configuration files. nameβ‰ place, bestie, don't get it twisted 🎯
  • -d level, --debug level β€” specify the debug level, either as a number or as the associated label. here are the levels, the whole spectrum 🌈:
    • 0: QUIET β€” no output except for fatal errors. 🀐
    • 1: NORMAL β€” the default value. outputs progress information. πŸšΆβ™€οΈ
    • 2: PEDANTIC β€” warns about configuration issues that might be errors. useful as a first step in debugging configurations. 🧐
    • 3: INFO β€” prints information about decisions during the build. good for debugging configurations. πŸ“‹
    • 4: VERBOSE β€” all information you will typically need when debugging configuration issues. πŸ—£οΈ
    • 5: DEBUG β€” information typically only needed when debugging mymake itself. πŸ›
  • --default-input input β€” add input as an input file if no other inputs were specified either on the command line or in any configuration files. this option is intended to be used when integrating mymake in an editor. the editor may then always supply the name of the current open file as a --default-input to make mymake do the right thing based on configurations: compiling the current file if nothing else is specified, otherwise follow the other, more precise instructions in the configuration files. editor besties, this one's for you πŸ’»πŸ’–
  • [options] β€” zero or more options may also be specified on the command line. these options may correspond to sections in one of the configuration files to enable (or disable). see the CONFIGURATION section below for details. on and off energy ✨
  • [files] β€” files may also be specified on the command line. mymake will add all names that correspond to existing files (possibly first appending known file extensions) to the list of input files to process. drop the names, it handles it πŸ“
  • --config β€” create the global configuration in ~/.config/mymake/mymake.conf (unless overridden using the XDG_CONFIG_HOME environment variable). this can also be used to revert the global configuration to the default state. will ask for the preferred number of threads to use during compilation, if -j is not specified earlier on the command line. 🌍
  • --target β€” creates the file .mymake in the current directory. it fills the file with a template configuration that contains common options and settings. 🎯
  • --project β€” creates the file .myproject in the current directory. the file contains a template suitable for a project, i.e. a directory consisting of multiple targets. πŸ—οΈ
  • --global-config β€” specifies the location of the global configuration file to use instead of the default (~/.config/mymake/mymake.conf). πŸ“

okay that's the setup, the vibes, and the flags β€” and there's more coming, so don't go anywhere bestie 🌈

πŸ’– CONFIGURATION πŸ’–

okay bestie, listen up, this is the part where mymake gets its whole wardrobe together. a mymake configuration is literally just a set of named variables, and every single one of those variables holds an array of strings. that's it. that's the tea. πŸ«–

so where do these values come from? they can come from one (or more) of four places, and here they are in the order that matters:

  1. the global configuration 🌍
  2. the project configuration (.myproject, if it's even there) πŸ“
  3. the target configuration (.mymake, if it's even there) 🎯
  4. the command-line ⌨️

and the project + target configs? they can live in the current directory or literally any parent directory. iconic. but here's the little plot twist: if a project configuration is found, then target configurations are expected to be sitting in sub-directories of the project configuration. so like, know your place, target configs. πŸ’…

options are always applied in the order above, no exceptions, no skipping. which means the global configuration can get overridden by the other configs, and the command-line parameters are always applied last because obviously the command line is the main character. πŸ‘‘

but wait 😭 there's one exception: when you're compiling a project, command line parameters are only applied when resolving the project configuration β€” they do not get applied when resolving the configuration for individual targets. remember that. it will come up.

🌸 the config file format 🌸

every single configuration file follows the exact same format, no cap. they're all a sequence of assignments to variables, and those can optionally be grouped into zero or more sections. lines that start with # are comments (so go ahead and annotate your chaos).

assignments come in one of these two forms:

name=value
name+=value
  • the first form replaces the entire contents of the variable with the string value. bye bye old data. πŸ™ˆ
  • the second form adds value as the last element of the array. that's the append queen behavior. πŸŽ€
  • and as a special little treat: if you use the first form and value is empty, the variable gets assigned the empty array. just so we're clear.

assignments can be grouped into sections by putting a section header before them. a section header looks like this:

[option1,option2,...]

what does it mean, bestie? it means: only consider the following assignments if every single option in that header is present in the context where the config is being evaluated (like, specified on the command line). so:

  • [] β€” always evaluated βœ… (empty header, no conditions, slays every time)
  • [release] β€” only evaluated if the release option has been specified
  • [release,unix] β€” only if both release and unix have been specified

and plot twist: you can prefix an option with ! to mean that option has to be absent. for example: [!release,unix]. that's the "not like the other options" syntax. πŸ’€


then mymake evaluates each configuration file in turn to build up the set of variables used during compilation. it does this by walking through every assignment, in every file, in the order they're specified, and just skipping any sections that should be skipped based on the options available. simple.

typically each file is only evaluated once, with a context made of the options you passed on the command-line. the exception here is .myproject-files, which get their own whole moment in the PROJECTS section below. πŸ“š


✨ PRE-DEFINED OPTIONS ✨

these options come pre-loaded, defined by mymake itself or by the default configuration, and you can use them right out of the box. no setup, we love a ready-made queen. πŸ‘‘

  • release β€” produce a release version of the program. this typically means turning on more aggressive optimizations. go fast or go home. πŸ”₯
  • lib β€” produces a static library. typically used in projects when some targets are static libraries used by other targets in the project. teamwork makes the dream work. 🀝
  • sharedlibe β€” produces a dynamic library. typically used in projects, like lib. ✨
  • unix β€” defined by mymake when compiling for a UNIX-like system. 🐧
  • windows β€” defined by mymake when compiling for a Windows-like system. πŸͺŸ
  • project β€” defined automatically when evaluating the .myproject file in the project context. πŸ“
  • build β€” defined automatically when evaluating the .myproject file to find options for the targets in a project. πŸ”¨
  • deps β€” defined automatically when evaluating the .myproject file to find explicit dependencies between projects. 🧡

🧬 VARIABLES 🧬

okay bestie, so these are the variables mymake uses to figure out what it's actually supposed to do. Some of them are special β€” like, mymake itself cares about them β€” and others get defined by the global configuration. And yes, you can totally define and use your own extra variables in config files, go off πŸ’…

  • ext β€” Array of the file extensions you want to compile. Whenever mymake notices you included x.h, it goes looking through every extension in ext to hunt down the matching implementation file πŸ•΅οΈβ€β™€οΈ
  • execExt β€” The file extension for executables. Gets slapped onto the output filename automatically, no cap.
  • intermediateExt β€” File extension for intermediate files. Standardly .o on UNIX systems.
  • buildDir β€” String with the directory where all your temporary build files live. It's relative to the root directory of the target (aka where the .mymake file is).
  • execDir β€” String with the directory where the final output (the executable) of all targets goes. Relative to the target's root directory.
  • ignore β€” Array of wildcard patterns (shell-style) deciding if a file should just be ignored. So useful when you're playing with templates, or when parts of the source code simply should not be compiled πŸ™…β€β™€οΈ
  • noIncludes β€” Array of wildcard patterns (again, shell-style) deciding if a path should NOT be scanned for headers. Chef's kiss when part of your code isn't C/C++ and looking for #include is literally meaningless.
  • input β€” Array of file names to use as roots when hunting for files that need compiling. Anything on the command line that isn't an option gets appended to this variable. The special value * means "compile all files with an extension in the ext variable" β€” which is usually what you want when you're compiling a library of some kind. ✨
  • output β€” String naming the output file. If you don't specify it, the name of the first input file gets used instead.
  • appendExt β€” Append the original extension of the original source file to the intermediate file when compiling. This lets mymake compile projects where multiple files share a name β€” like foo.cpp and foo.c β€” without both of them trying to create foo.o and tanking your whole compilation. mymake will warn you if you might need this one πŸ‘€
  • include β€” Array of paths to add to the compilation's include path.
  • includeCl β€” Flag to prepend all the elements in include.
  • includes β€” Generated automatically by mymake; it's equivalent to adding the contents of includeCl before each element in include.
  • library β€” Array of system libraries to link to your executable.
  • libraryCl β€” Flag to prepend all the elements in library.
  • localLibrary β€” Array of local libraries to link to your executable (usually used in a project).
  • localLibraryCl β€” Flag to prepend all the elements in localLibrary.
  • libs β€” Automatically generated by mymake, equivalent to adding libraryCl before all elements of library, also including local libraries.
  • define β€” Preprocessor defines.
  • defineCl β€” Preprocessor define flag.
  • execute β€” Yes or no, telling mymake whether it should run the program after a successful compilation. Can be overridden on the command line with -e or -ne.
  • pch β€” The precompiled header file name that should be used. If you're on the default config, you only need to set this variable to use precompiled headers. And if you're using #pragma once in gcc, you will sadly get a warning that seems genuinely impossible to disable (it's not a problem when precompiling headers though, so breathe).
  • pchFile β€” The name of the compiled version of the file in pch.
  • pchCompile β€” Command line for compiling the precompiled header file.
  • pchCompileCombined β€” If set to yes, pchCompile is expected to generate both the pch-file and compile a .cpp-file. multitasking icon πŸ’…
  • preBuild β€” Array of command-lines to run before the build starts. Expands variables.
  • preBuildCreates β€” Array of files created by the pre-build step which should also be included in the compilation. These are expected not to introduce any additional dependencies into the project, since they literally aren't available yet at the point where mymake resolves dependencies between files and targets.
  • postBuild β€” Array of command-lines to run after the build is completed. Expands variables.
  • compile β€” Array of command lines to use when compiling files. Each command line starts with a pattern (ending in :) that gets matched against the file name to be compiled. The command line added last is checked first, and the first matching command-line wins. So it's useful to add the general command-line first (starting with *:), then add the more specific ones. Here you can use <file> for the input file and <output>.
  • link β€” Command line used when linking the intermediate files. Use <files> for all input files and <output> for the output file-name.
  • linkOutput β€” Link the output of one target to any target that depends on that target. See projects for more info.
  • forwardDeps β€” Forward any of this target's dependencies to any target that depends on this target.
  • env β€” Set environment variables. Each element in env is expected to be in the form: variable=value, variable<=value, or variable=>value. First form replaces the environment variable variable with value. Second form prepends value to variable using the system's separator (: on unix, ; on windows). Third form appends value to variable. The second and third forms are super convenient when you're working with PATH, for example 🌸
  • explicitTargets β€” In projects: ignore any potential targets that don't have their own .mymake-file.
  • parallel β€” In projects, this says whether projects that have all their dependencies satisfied can be built in parallel. Default value is yes, so projects that can't tolerate parallel builds may set it to no. In targets, this says whether files within targets may be built in parallel. If so, all input files β€” except precompiled headers β€” are built in parallel using up to maxThreads threads globally. If specific targets can't tolerate this, set parallel to no and mymake will build those targets in serial, no drama.
  • maxThreads β€” Limits the global number of threads (which are actually processes, bestie) used to build the project/target globally.
  • usePrefix β€” When building in parallel, add a prefix to the output that corresponds to different targets. Defaults to either vc or gnu (depends on your system). If you set it to no, no prefix gets added. vc adds n> before output, gnu adds pn: before output. This is so Emacs can recognize the error messages from the vc and gnu compilers respectively. thoughtfulness πŸ’–
  • absolutePath β€” Send absolute paths to the compiler. This helps emacs find the proper source files in projects with multiple targets.
  • implicitDeps β€” (defaults to yes) if set, mymake tries to figure out dependencies between targets by looking at includes. Sometimes this results in unneeded circular dependencies, which makes compilation fail 😭 so sometimes it's necessary to set this to no.

πŸ’€ IMPORTANT ONE, PLS READ: every variable that contains commands to execute can be prefixed with an integer followed by a colon (for example 1:gcc). If prefixed with the integer N, the first N lines of output from that command get removed from the output. So basically you can use this to ignore status info from commands during the build without ignoring the actually useful info. Example: the C++ compiler on Windows outputs a line containing the filename being compiled by default. That's unnecessary since mymake already does this. So it can be suppressed by issuing the command 1:cl ....

This feature is possible to use in the variables compile, pchCompile, and link.

πŸͺ„ VARIABLES IN STRINGS πŸͺ„

When mymake uses some variables (most notably, the compilation and link command lines) it looks at each string and recursively replaces any variables that appear there. Note that this is not done when the configuration is evaluated β€” only when the variables are actually used. timing is everything, queen ✨

Any occurrences of <variable> get replaced with the contents of variable. It's also possible to prepend a string to each element in another variable using the syntax <prefix*variable>, which means the string in the variable prefix gets prepended to each element in the variable variable.

The special variable <env:X> (where X is any string) can be used to extract the value of environment variables. This is useful to, say, inspect the value of CFLAGS during compilation. The benefit of using this syntax over relying on shell expansion is that it lets mymake track changes in the environment variables. πŸ‘‘

It's also possible to perform an operation on each element in the array using the syntax <op|variable>. And yes, you can both perform an operation and prepend data using <prefix*op|variable>. Supported operations are:

  • title β€” Treat the element as a path and extract the file or directory name (e.g. src/foo.txt gives foo.txt).
  • titleNoExt β€” Same as title, but the file extension is removed as well.
  • noExt β€” Remove the file extension from a path.
  • path β€” Format the element as a path for the current operating system. For example src/foo.txt evaluates to src\foo.txt on Windows.
  • buildpath β€” Make the element into a path inside the build path.
  • execpath β€” Make the element into a path inside the executable path.
  • parent β€” Evaluates to the parent directory of the path. If no parent is given (e.g. only a file name), the element is removed from the array.
  • if β€” Make all elements empty. This can be used to test if a variable contains a value and then include some other text. For example <usePch*if|pchFile> to add the flag inside usePch if a file is specified in pchFile.

🌸 PROJECTS 🌸

okay so bestie, a project is literally just a little squad of targets all linked together by a .myproject-file 🫢

and here's the ✨iconic✨ part: that .myproject file does not get read just once. it gets evaluated multiple times, each time with different options present, so mymake can extract different info about the project out of it. it's giving multiple costume changes, one file, many eras πŸ’…


πŸ”¨ build

build β€” mymake evaluates the .myproject-file once with the build option set. and uniquely to this pass, only the parts that explicitly mention build are extracted. no mention, no entry, periodt πŸ’€

after that, mymake expects to find a list of options to apply to each of the sub-projects. for example, a project file often contains a section like this:

[build]
main+=debug
libfoo+=lib
libfoo+=debug

so in this case we're instructing mymake to build the main target with the debug option present, and the target libfoo with the options debug and lib. simple as that πŸ’–

also there's a special target called all β€” whatever options it's given will apply to all targets in the project. every last one. no skipping πŸ™ˆ

from version 2.4.5 and onwards of mymake, it's also possible to specify build options in the target's .mymake file by assigning additional options to the self variable inside a build block. so for example, the options to libfoo above could be specified in libfoo's .mymake file like this:

[build]
self+=lib
self+=debug

so now the target can lowkey dress itself πŸ”₯

πŸ”— deps

deps β€” this option is specified during one evaluation to extract explicit dependencies between projects. by default, mymake finds dependences between projects automatically by examining includes across projects. in certain cases though, it's genuinely useful to introduce extra dependencies, just to make sure that some dynamically loaded parts are also built. we've all been there 🫠

this section is very similar to the build section. mymake expects to find variables corresponding to each target, and that these variables contains names of other targets. and as with build, only sections that explicitly mention deps are considered. it's also possible to define explicit dependencies in the per-target .mymake file using self+=Dependency as above. same energy, different section πŸ¦‹

if one target results in a library, it's convenient to set the variable linkOutput to yes for that target. mymake will then add the output of the library target to the library variable of any targets that depend on it. chef's kiss πŸ‘‘


and after mymake has extracted the necessary project information? the .myproject-file is also evaluated once for each target 🌈 which means yep β€” it's totally possible to specify additional variables or options that apply to all targets in the project, right there in the .myproject file. slay.


and that's the whole page, bestie πŸ’œ this edition of MYMAKE(1) is dated August 4 2026 β€” go build something iconic, i'll be here πŸŽ€βœ¨

πŸ“¦ where this page comes from