ZenMake documentation¶
ZenMake - build system based on the meta build system Waf.
Introduction¶
What is it?¶
ZenMake is a cross-platform build system for C/C++ and some other languages. It uses meta build system Waf as a framework.
Some reasons to create this project can be found here.
Main features¶
- Build config as python (.py) or as yaml file. Details are here.
- Distribution as zip application or as system package (pip). See Installation.
- Automatic reconfiguring: no need to run command ‘configure’.
- Compiler autodetection.
- Building and running functional/unit tests including an ability to build and run tests only on changes. Details are here.
- Build configs in sub directories.
- Building external dependencies.
- Supported platforms: GNU/Linux, MacOS, MS Windows. Some other platforms like OpenBSD/FreeBSD should work as well but it hasn’t been tested.
- Supported languages:
- C: gcc, clang, msvc, icc, xlc, suncc, irixcc
- C++: g++, clang++, msvc, icpc, xlc++, sunc++
- D: dmd, ldc2, gdc; MS Windows is not supported yet
- Fortran: gfortran, ifort (should work but not tested)
- Assembler: gas (GNU Assembler)
- Supported toolkits/frameworks: - SDL v2 (Linux only) - GTK v3 (Linux only) - Qt v5
Plans to do¶
There is no clear roadmap for this project. I add features that I think are needed to include.
Project links¶
- Primary git repository: https://github.com/pustotnik/zenmake
- Secondary git repository: https://gitlab.com/pustotnik/zenmake
- Issue tracker: https://github.com/pustotnik/zenmake/issues
- Pypi package: https://pypi.org/project/zenmake
- Documentation: https://zenmake.readthedocs.io
Why?¶
Short answer: because I could and wanted.
Long answer is below.
https://news.ycombinator.com/item?id=18789162
Cool. One more "new" build system...
Yes, I know, we already have a lot of them. I decided to create this project because I couldn’t find a build tool for Linux which is quick and easy to use, flexible, ready to use, with declarative configuration, without the need to learn one more special language and suitable for my needs. I know about lots of build systems and I have tried some of them.
Well, a little story of the project. In 2010 year I developed a build system in a company where I was working that time. It was a build system based on Waf and it was used successfully for linux projects several years. But that system had a lot of internal problems and I wanted to remake it from scratch. And in 2013 year I tried to begin a new project. But I had no time to develop it at that time. Then, in 2019 year I decided to make some own opensorce project and was selecting a build system for my project. I was considering only opensource cross-platform build systems that can build C/C++ projects on GNU/Linux. Firstly I tried CMake, then Meson and Waf. Also I was looking at some other build systems like Bazel. Eventually, I concluded that I had to try to make my own build tool.
I would do it mostly for myself, but I would be glad if my tool was useful for others.
Quickstart guide¶
To use ZenMake you need ZenMake and buildconf file in the root of your project.
Let’s consider an example with this structure:
testproject
├── buildconf.yml
├── prog
│ └── test.cpp
└── shlib
├── util.cpp
└── util.h
For this project buildconf.yml
can be like that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | tasks: util : features : cxxshlib source : 'shlib/**/*.cpp' includes : '.' program : features : cxxprogram source : 'prog/**/*.cpp' includes : '.' use : util buildtypes: debug : toolchain : clang++ cxxflags : -O0 -g release : toolchain : g++ cxxflags : -O2 default : debug |
Lines | Description |
---|---|
1 | Section with build tasks |
2,6 | Names of build tasks. By default they are used as target names. Resulting target names will be adjusted depending on a platform. For example, on Windows ‘program’ will result to ‘program.exe’. |
3 | Mark build task as a C++ shared library. |
4 | Specify all *.cpp files in the directory ‘shlib’ recursively. |
5,9 | Specify the path for C/C++ headers relative to the project root directory. In this example, this parameter is optional as ZenMake adds the project root directory itself. But it’s an example. |
7 | Mark build task as a C++ executable. |
8 | Specify all *.cpp files in the directory ‘prog’ recursively. |
10 | Specify task ‘util’ as dependency to task ‘program’. |
12 | Section with build types. |
13,16 | Names of build types. They can be almost any. |
14 | Specify Clang C++ compiler for debug. |
15 | Specify C++ compiler flags for debug. |
17 | Specify g++ compiler (from GCC) for release. |
18 | Specify C++ compiler flags for release. |
19 | Special case: specify default build type that is used when no build type was specified for ZenMake command. |
In case of using python config the file buildconf.py
with the same values as above
would look like this:
tasks = {
'util' : {
'features' : 'cxxshlib',
'source' : 'shlib/**/*.cpp',
'includes' : '.',
},
'program' : {
'features' : 'cxxprogram',
'source' : 'prog/**/*.cpp',
'includes' : '.',
'use' : 'util',
},
}
buildtypes = {
'debug' : {
'toolchain' : 'clang++',
'cxxflags' : '-O0 -g',
},
'release' : {
'toolchain' : 'g++',
'cxxflags' : '-O2',
},
'default' : 'debug',
}
Once you have the config, run zenmake
in the root of the project and
ZenMake does the build:
$ zenmake
* Project name: 'testproject'
* Build type: 'debug'
Setting top to : /tmp/testproject
Setting out to : /tmp/testproject/build
Checking for 'clang++' : /usr/lib/llvm/11/bin/clang++
[1/4] Compiling shlib/util.cpp
[2/4] Compiling prog/test.cpp
[3/4] Linking build/debug/libutil.so
[4/4] Linking build/debug/program
'build' finished successfully (0.531s)
Running ZenMake without any parameters in a directory with buildconf.py
or
buildconf.yml
is the same as running zenmake build
. Otherwise it’s
the same as zenmake help
.
Get the list of all commands with a short description using
zenmake help
or zenmake --help
. To get help on selected command you
can use zenmake help <selected command>
or
zenmake <selected command> --help
For example to build release
of the project above such a command can
be used:
$ zenmake build -b release
* Project name: 'testproject'
* Build type: 'release'
Setting top to : /tmp/testproject
Setting out to : /tmp/testproject/build
Checking for 'g++' : /usr/bin/g++
[1/4] Compiling shlib/util.cpp
[2/4] Compiling prog/test.cpp
[3/4] Linking build/release/libutil.so
[4/4] Linking build/release/program
'build' finished successfully (0.498s)
Here is some possible variant of extended version of the config from above:
tasks:
util :
features : cxxshlib
source : 'shlib/**/*.cpp'
includes : '.'
libs : boost_timer # <-- Add the boost timer library as dependency
program :
features : cxxprogram
source : 'prog/**/*.cpp'
includes : '.'
use : util
buildtypes:
debug :
toolchain : clang++
cxxflags : -O0 -g
release :
toolchain : g++
cxxflags : -O2
default : debug
configure:
- do: check-headers
names : cstdio iostream # <-- Check C++ 'cstdio' and 'iostream' headers
- do: check-libs # <-- Check all libraries from the 'libs' parameter
One of the effective and simple ways to learn something is to use
real examples. So it is recommended to look at examples in demos
directory
which can be found in the repository here.
Installation¶
Dependencies
- Python >=3.5. Python must have threading support.
Python has threading in most cases while nobody uses
--without-threads
for Python building. Python >= 3.7 always has threading. - PyYAML It’s optional and needed only if you use yaml buildconf. ZenMake can be used with yaml buildconf file even with PyYAML not installed in an operating system because ZenMake has an internal copy of PyYAML python library. This copy is used only if there is no PyYAML installed in an operating system.
There are different ways to install/use ZenMake:
Via python package (pip)¶
ZenMake has its own python package. You can install it by:
pip install zenmake
In this way pip will install PyYAML if it’s not installed already.
Note
POSIX
: It requires root and will install it system-wide.
Alternatively, you can use:
pip install --user zenmake
which will install it for your user and does not require any special privileges. This will install the package in ~/.local/, so you will have to add ~/.local/bin to your PATH.
Windows
: It doesn’t always require administrator rights.
Note
You need to have pip
installed. Most of the modern Linux distributions
have pip in their packages. On Windows you can use, for example,
chocolatey to install pip.
Common instructions to install pip can be found
here.
Note
You can install ZenMake with pip and virtualenv. In this case you don’t touch system packages and it doesn’t require root privileges.
After installing you can run ZenMake just by typing:
zenmake
Via git¶
You can use ZenMake from Git repository. But branch master
can be
broken. Also, you can just to switch to the required version using git tag. Each
version of ZenMake has a git tag. The body of ZenMake application is located in
src/zenmake
path in the repository. You don’t need other directories and
files in repository and you can remove them if you want. Then you can make symlink
to src/zenmake/zmrun.py
, shell alias or make executable
.sh script (for Linux/MacOS/..) or .bat (for Windows) to
run ZenMake. Example for Linux (zmrepo
is custom directory):
$ mkdir zmrepo
$ cd zmrepo
$ git clone https://github.com/pustotnik/zenmake.git .
Next step is optional. Switch to existing version, for example to 0.7.0:
$ git checkout v0.7.0
Here you can make symlink/alias/script to run zenmake.
Other options to run ZenMake:
$ <path-to-zenmake-repo>/src/zenmake/zmrun.py
or:
$ python <path-to-zenmake-repo>/src/zenmake
As a zip application¶
Zenmake can be run as an executable python zip application. And ZenMake can make
such zipapp with the command zipapp
.
Using steps from Via Git you can run:
$ python src/zenmake zipapp
$ ls *.pyz
zenmake.pyz
$ ./zenmake.pyz
...
Resulting file zenmake.pyz
can be run standalone without the repository and pip.
You can copy zenmake.pyz
to the root of your project and distribute this
file with your project. It can be used on any supported platform and doesn’t
require any additional access and changes in your system.
Note
Since ZenMake 0.10.0 you can download ready to use zenmake.pyz
from
GitHub releases.
Build config¶
ZenMake uses build configuration file with name buildconf.py
or
buildconf.yaml
/buildconf.yml
. First variant is a regular python file and second one is
an YAML file. ZenMake doesn’t use both files in one directory at the same time.
If both files exist in one directory then only buildconf.py
will be used.
Common name buildconf
is used in this manual.
The format for both config files is the same. YAML variant is a little more readable but in python variant you can add a custom python code if you wish.
Simplified scheme of buildconf is:
startdir = path buildroot = path realbuildroot = path project = { ... } general = { ... } cliopts = { ... } conditions = { ... } tasks = { name: task parameters } buildtypes = { name: task parameters } toolchains = { name: parameters } byfilter = [ { for: {...}, set: task parameters }, ... ] subdirs = [] edeps = { ... }
Also see syntactic sugar.
Where symbols ‘{}’ mean an associative array/dictionary and symbols ‘[]’
mean a list. In python notation ‘{}’ is known as dictionary. In some other
languages it’s called an associative array including YAML (Of course YAML is not
programming language but it’s markup language). For shortness it’s called
a dict
here.
Not all variables are required in the scheme above but buildconf cannot be empty. All variables have reserved names and they all are described here. Other names in buildconf are just ignored by ZenMake (excluding substitution variables) if present and it means they can be used for some custom purposes.
Note
About paths in general.
You can use native paths but it’s recommended to use wherever possible
POSIX paths (Symbol /
is used as a separator in a path).
With POSIX paths you will ensure the same paths on
different platforms/operating systems. POSIX paths will be
converted into native paths automatically, but not vice versa.
For example, path ‘my/path’ will be converted into ‘my\path’ on Windows.
Also it’s recommended to use relative paths wherever possible.
Warning
Windows only
: do NOT use short filename notation (8.3 filename) for
paths in buildconf files. It can cause unexpected errors.
Below is the detailed description of each buildconf variable.
startdir¶
A start path for all paths in a buildconf. It is.
by default. The path can be absolute or relative to directory where current buildconf file is located. It means that by default all other relative paths in the current buildconf file are considered as the paths relative to directory with the current buildconf file. But you can change this by setting a different value to this variable.
buildroot¶
A path to the root of a project build directory. By default it is directory ‘build’ in the directory with the top-level buildconf file of the project. Path can be absolute or relative to the startdir. It is important to be able to remove the build directory safely, so it should never be given as
.
or..
.Note
If you change value of
buildroot
with already using/existing build directory then ZenMake will not touch previous build directory. You can remove previous build directory manually or run commanddistclean
before changing ofbuildroot
. ZenMake cannot do it because it stores all meta information in current build directory and if you change this directory it will lose all such an information.This can be changed in the future by storing extra information in some other place like user home directory but now it is.
realbuildroot¶
A path to the real root of a project build directory and by default it is equal to value ofbuildroot
. It is optional parameter and ifrealbuildroot
has different value frombuildroot
thenbuildroot
will be symlink torealbuildroot
. Usingrealbuildroot
makes sense mostly on linux where ‘/tmp’ is usually on tmpfs filesystem nowadays and it can used to make building in memory. Such a way can improve speed of building. Note that on Windows OS the process of ZenMake needs to be started with enabled “Create symbolic links” privilege and usual user doesn’t have a such privilege. Path can be absolute or relative to the startdir. It is important to be able to remove the build directory safely, so it should never be given as.
or..
.
project¶
general¶
A dict array with some general features. Supported values:
autoconfig: Execute the command
configure
automatically in the commandbuild
if it’s necessary. It’sTrue
by default. Usually you don’t need to change this value.monitor-files: Set extra file paths to check changes in them. You can use additional files with your buildconf file(s). For example it can be extra python module with some tools. But in this case ZenMake doesn’t know about such files when it checks buildconf file(s) for changes to detect if it must call command
configure
for featureautoconfig
. You can add such files to this variable and ZenMake will check them for changes as it does so for regular buildconf file(s).If paths contain spaces and all these paths are listed in one string then each such a path must be in quotes.
hash-algo: Set hash algorithm to use in ZenMake. It can be
sha1
ormd5
. By default ZenMake uses sha1 algorithm to control changes of config/built files and for some other things. Sha1 has much less collisions than md5 and that’s why it’s used by default. Modern CPUs often has support for this algorithm and sha1 show better or almost the same performance than md5 in this cases. But in some cases md5 can be faster and you can set here this variant. However, don’t expect big difference in performance of ZenMake. Also, if a rare “FIPS compliant” build of Python is used it’s always sha1 anyway.db-format: Set format for internal ZenMake db/cache files. Use one of possible values:
py
,pickle
,msgpack
.The value
py
means text file with python syntax. It is not fastest format but it is human readable one.The value
pickle
means python pickle binary format. It has good performance and python always supports this format.The value
msgpack
means msgpack binary format by using python modulemsgpack
. Using of this format can decrease ZenMake overhead in building of some big projects because it has the best performance among all supported formats. If the packagemsgpack
doesn’t exist in the current system then thepickle
value will be used. Note: ZenMake doesn’t try to install packagemsgpack
. This package must be installed in some other way.The default value is
pickle
.provide-edep-targets: Provide target files of external dependencies in the buildroot directory. It is useful to run built files from the build directory without the need to use such a thing as LD_LIBRARY_PATH for each dependency. Only existing and used target files are provided. Static libraries are also ignored because they are not needed to run built files. On Windows ZenMake copies these files while on other OS (Linux, MacOS, etc) it makes symlinks.
It’s
False
by default.build-work-dir-name: Set a name of work directory which is used mostly for object files during compilation. This directory seperates resulting target files from other files in a buildtype directory to avoid file/directory conflicts. Usually you don’t need to set this parameter until some target name has conflict with default value of this parameter.
The default value is
@bld
.
cliopts¶
A dict array with default values for command line options. It can be any existing command line option that ZenMake has. If you want to set an option for selected commands then you can set it in the format of a dict where key is a name of specific command or special value ‘any’ which means any command. If some command doesn’t have selected option then it will be ignored.
Example in YAML format:
cliopts: verbose: 1 jobs : { build : 4 } progress : any: false build: trueNote
Selected command here is a command that is used on command line. It means if you set an option for the
build
command and zenmake calls theconfigure
command before this command by itself then this option will be applied for bothconfigure
andbuild
commands. In other words it is like you are running this command with this option on command line.
conditions¶
A dict with conditions for selectable parameters.
tasks¶
A dict with build tasks. Each task has own unique name and parameters. Name of task can be used as dependency id for other build tasks. Also this name is used as a base for resulting target file name if parameter
target
is not set in task parameters. In this variable you can set up build parameters particularly for each build task. Example in YAML format:tasks: mylib : # some task parameters myexe : # some task parameters use : mylibNote
Parameters in this variable can be overridden by parameters in buildtypes and/or byfilter.
Note
Name of a task cannot contain symbol
:
. You can use parametertarget
if you want to have this symbol in resulting target file names.
buildtypes¶
A dict with build types like
debug
,release
,debug-gcc
and so on. Here is also a special value with namedefault
that is used to set default build type if nothing is specified. Names of these build types are just names, they can be any name but notdefault
. Also you should remember that these names are used as directory names. So don’t use incorrect symbols if you don’t want a problem with it.This variable can be empty or absent. In this case current buildtype is always just an empty string.
Possible parameters for each build type are described in task parameters.
Special value
default
must be a string with the name of one of the build types or a dict where keys are supported name of the host operating system and values are strings with the names of one of the build types. Special key ‘_’ or ‘no-match’ can be used in thedefault
to define a value that will be used if the name of the current host operating system is not found among the keys in thedefault
.Valid host operating system names:
linux
,windows
,darwin
,freebsd
,openbsd
,sunos
,cygwin
,msys
,riscos
,atheos
,os2
,os2emx
,hp-ux
,hpux
,aix
,irix
.Note
Only
linux
,windows
anddarwin
are tested.Examples in YAML format:
buildtypes: debug : { toolchain: auto-c++ } debug-gcc : { toolchain: g++, cxxflags: -fPIC -O0 -g } release-gcc : { toolchain: g++, cxxflags: -fPIC -O2 } debug-clang : { toolchain: clang++, cxxflags: -fPIC -O0 -g } release-clang: { toolchain: clang++, cxxflags: -fPIC -O2 } debug-msvc : { toolchain: msvc, cxxflags: /Od /EHsc } release-msvc : { toolchain: msvc, cxxflags: /O2 /EHsc } default: debug buildtypes: debug: toolchain.select: default: g++ darwin: clang++ windows: msvc cxxflags.select: default : -O0 -g msvc : /Od /EHsc release: toolchain.select: default: g++ darwin: clang++ windows: msvc cxxflags.select: default : -O2 msvc : /O2 /EHsc default: debug buildtypes: debug-gcc : { cxxflags: -O0 -g } release-gcc : { cxxflags: -O2 } debug-clang : { cxxflags: -O0 -g } release-clang: { cxxflags: -O2 } debug-msvc : { cxxflags: /Od /EHsc } release-msvc : { cxxflags: /O2 /EHsc } default: _: debug-gcc linux: debug-gcc darwin: debug-clang windows: debug-msvc
toolchains¶
A dict with custom toolchain setups. It’s useful for simple cross builds for example, or for custom settings for existing toolchains. Each value has unique name and parameters. Parameters are also dict with names of environment variables and special name
kind
that is used to specify the type of toolchain/compiler. Environment variables are usually such variables asCC
,CXX
,AR
, etc that are used to specify name or path to existing toolchain/compiler. Path can be absolute or relative to the startdir. Also such variables asCFLAGS
,CXXFLAGS
, etc can be set here.Names of toolchains from this parameter can be used as a value for the
toolchain
in task parameters.Example in YAML format:
toolchains: custom-g++: kind : auto-c++ CXX : custom-toolchain/gccemu/g++ AR : custom-toolchain/gccemu/ar custom-clang++: kind : clang++ CXX : custom-toolchain/clangemu/clang++ AR : custom-toolchain/clangemu/llvm-ar g++: LINKFLAGS : -Wl,--as-needed
byfilter¶
This variable describes extra/alternative way to set up build tasks. It’s a list of dicts with attributes
set
andfor
,not-for
and/orif
. Attributesfor
/not-for
/if
describe conditions for parameters in attributeset
, that is, a filter to set some build task parameters. The attributefor
is like aif a
and the attributenot-for
is like aif not b
wherea
andb
are some conditions. And they are like aif a and if not b
when both of them exist in the same item. The attributenot-for
has higher priority in the case of the same condition in the both of them.Since v0.11 ZenMake supports
if
attribute where you can set a string with python like expression.The
for
/not-for
are dicts andif
is an expression. Infor
/not-for
/if
you can use such variables as dict keys infor
/not-for
and as keywords within an expression:
task: Build task name or list of build task names. It can be existing task(s) from tasks or new (only in for
).buildtype: Build type or list of build types. It can be existing build type(s) from buildtypes or new (only in for
).platform: Name of a host platform/operating system or list of them. Valid values are the same as for default
in buildtypes.The
if
is a real python expression with some builtin functions. You can use standard python operators as ‘(’, ‘)’, ‘and’, ‘or’, ‘not’, ‘==’, ‘!=’ and ‘in’. ZenMake supports a little set of extensions as well:
Name Description true The same as python ‘True’. false The same as python ‘False’. startswith(str, prefix) Returns true if ‘str’ starts with the specified ‘prefix’. endswith(str, prefix) Returns true if ‘str’ ends with the specified ‘suffix’. The attribute
set
has value of the task parameters.Other features:
- If some key parameter is not specified in
for
/not-for
/if
it means that this is for all possible values of this kind of condition. For example if it has notask
it means ‘for all existing tasks’. Special wordall
(without any other parameters) can be used to indicate that current item must be applied to all build tasks. Empty dict (i.e.{}
) infor
/not-for
can be used for the same reason as well.- Variable ‘byfilter’ overrides all matched values defined in tasks and buildtypes.
- Items in
set
with the same names and the same conditions infor
/not-for
/if
override items defined before.- If
for
/not-for
andif
are used for the sameset
then result will be the intersection of resulting sets fromfor
/not-for
andif
.- When
set
is empty or not defined it does nothing.Note
ZenMake applies every item in the
byfilter
in the order as they were written.It’s possible to use
byfilter
without tasks and buildtypes.Example in YAML format:
GCC_BASE_CXXFLAGS: -std=c++11 -fPIC buildtypes: debug-gcc : { cxxflags: $GCC_BASE_CXXFLAGS -O0 -g } release-gcc : { cxxflags: $GCC_BASE_CXXFLAGS -O2 } debug-clang : { cxxflags: $GCC_BASE_CXXFLAGS -O0 -g } release-clang: { cxxflags: $GCC_BASE_CXXFLAGS -O2 } debug-msvc : { cxxflags: /Od /EHsc } release-msvc : { cxxflags: /O2 /EHsc } default: _: debug-gcc linux: debug-gcc darwin: debug-clang windows: debug-msvc byfilter: - for: all set: { includes: '.', rpath : '.', } - for: { task: shlib shlibmain } set: { features: cxxshlib } - for: { buildtype: debug-gcc release-gcc, platform: linux } set: toolchain: g++ linkflags: -Wl,--as-needed - for: { buildtype: release-gcc } not-for : { platform : windows } set: { cxxflags: -fPIC -O3 } - for: { buildtype: [debug-clang, release-clang], platform: linux darwin } set: { toolchain: clang++ } - if: endswith(buildtype, '-gcc') and platform == 'linux' set: toolchain: g++ linkflags: -Wl,--as-needed - if: buildtype == 'release-gcc' and platform == 'linux' set: cxxflags: $GCC_BASE_CXXFLAGS -O3 - if: endswith(buildtype, '-clang') and platform in ('linux', 'darwin') set: toolchain: clang++ - if: endswith(buildtype, '-msvc') and platform == 'windows' set: toolchain: msvcNote
Parameters in this variable override corresponding parameters in tasks and in buildtypes.
subdirs¶
This variable controls including buildconf files from other sub directories of the project.
- If it is list of paths then ZenMake will try to use this list as paths to sub directories with the buildconf files and will use all found ones. Paths can be absolute or relative to the startdir.
- If it is an empty list or just absent at all then ZenMake will not try to use any sub directories of the project to find buildconf files.
Example in Python format:
subdirs = [ 'libs/core', 'libs/engine', 'main', ]Example in YAML format:
subdirs: - libs/core - libs/engine - mainSee some details here.
Build config: task parameters¶
It’s a dict as a collection of build task parameters for a build task. This collection is used in tasks, buildtypes and byfilter. And it’s core buildconf element.
Also see substitutions for string/text values.
features¶
It describes type of a build task. Can be one value or list of values. Supported values:
c: Means that the task has a C code. Optional in most cases. Also it’s ‘lang’ feature for C language. cxx: Means that the task has a C++ code. Optional in most cases. Also it’s ‘lang’ feature for C++ language. d: Means that the task has a D code. Optional in most cases. Also it’s ‘lang’ feature for D language. fc: Means that the task has a Fortran code. Optional in most cases. Also it’s ‘lang’ feature for Fortran language. asm: Means that the task has an Assembler code. Optional in most cases. Also it’s ‘lang’ feature for Assembler language. <lang>stlib: Means that result of the task is a static library for the <lang> code. For example: cstlib
,cxxstlib
, etc.<lang>shlib: Means that result of the task is a shared library for the <lang> code. For example: cshlib
,cxxshlib
, etc.<lang>program: Means that result of the task is an executable file for the <lang> code. For example: cprogram
,cxxprogram
, etc.runcmd: Means that the task has the run
parameter and should run some command. It’s optional because ZenMake detects this feature automatically by presence of therun
in task parameters. You need to set it explicitly only if you want to try to run <lang>program task without parameterrun
.test: Means that the task is a test. More details about tests are here. It is not needed to add runcmd
to this feature because ZenMake addsruncmd
itself if necessary.qt5: Means that the task has Qt5 code. More details are here. Some features can be mixed. For example
cxxprogram
can be mixed withcxx
for C++ build tasks but it’s not necessary because ZenMake addscxx
forcxxprogram
itself. Thecxxshlib
feature cannot be mixed for example with thecxxprogram
in one build task because they are different types of build task targets. Using of such features asc
orcxx
doesn’t make sense without *stlib/*shlib/*program features in most cases. Theruncmd
andtest
features can be mixed with any feature.Examples in YAML format:
features : cprogram features : cxxshlib features : cxxprogram runcmd features : cxxprogram testExamples in Python format:
'features' : 'cprogram' 'features' : 'cxxshlib' 'features' : 'cxxprogram runcmd' 'features' : 'cxxprogram test'
target¶
Name of resulting file. The target will have different extension and name depending on the platform but you don’t need to declare this difference explicitly. It will be generated automatically. For example the
sample
for *shlib task will be converted intosample.dll
on Windows and intolibsample.so
on Linux. By default it’s equal to the name of the build task. So in most cases it is not needed to be set explicitly.It’s possible to use selectable parameters to set this parameter.
source¶
One or more source files for compiler/toolchain/toolkit. It can be:
- a string with one or more paths separated by space
- a dict, description see below
- a list of items where each item is a string with one or more paths or a dict
The
dict
type is used forant_glob
Waf function. Format of patterns forant_glob
can be found here https://waf.io/book/. Most significant details from there:
- Patterns may contain wildcards such as * or ?, but they are Ant patterns, not regular expressions.
- The symbol
**
enable recursion. Complex folder hierarchies may take a lot of time, so use with care.- The ‘..’ sequence does not represent the parent directory.
So such a
dict
can contain fields:
incl: Ant pattern or list of patterns to include, required field. excl: Ant pattern or list of patterns to exclude, optional field. ignorecase: Ignore case while matching (False by default), optional field. startdir: Start directory for patterns, optional field. It must be relative to the startdir or an absolute path. By default it’s ‘.’, that is, it’s equal to startdir. ZenMake always adds several patterns to exclude files for any ant pattern. These patterns include Default Excludes from Ant patterns and some additional patterns like
**/*.swp
.There is simplified form of ant patterns using: if string value contains ‘*’ or ‘?’ it will be converted into
dict
form to use patterns. See examples below.Any path or pattern should be relative to the startdir. But for pattern (in dict) can be used custom
startdir
parameter.Note
If paths contain spaces and all these paths are listed in one string then each such a path must be in quotes.
YAML: You can write a string without quotes (as a plain scalar) in many cases but there are some special symbols which cannot be used at the beginning without quotes, for example
*
and?<space>
. So a value like**/*.cpp
must be always in qoutes ('
or"
).See details here: https://www.yaml.info/learn/quote.html.
Examples in YAML format:
# just one file source : test.cpp # list of two files source : main.c about.c # or source : [main.c, about.c] # get all *.cpp files in the 'startdir' recursively source : { incl: '**/*.cpp' } # or source : incl: '**/*.cpp' # or (shortest record with the same result) source : '**/*.cpp' # get all *.c and *.cpp files in the 'startdir' recursively source : { incl: '**/*.c **/*.cpp' } # or (shorter record with the same result) source : '**/*.c **/*.cpp' # get all *.cpp files in the 'startdir'/mylib recursively source : mylib/**/*.cpp # get all *.cpp files in the 'startdir'/src recursively # but don't include files according pattern 'src/extra*' source : incl: src/**/*.cpp excl: src/extra* # get all *.c files in the 'src' and in '../others' recursively source : - src/**/*.c - incl: '**/*.c' startdir: ../others # pattern with space, it's necessary to use both types of quotes here: source : '"my prog/**/*.c"' # two file paths with spaces source : '"my shlib/my util.c" "my shlib/my util2.c"'Examples in python format:
# just one file 'source' : 'test.cpp' # list of two files 'source' : 'main.c about.c' 'source' : ['main.c', 'about.c'] # the same result # get all *.cpp files in the 'startdir' recursively 'source' : dict( incl = '**/*.cpp' ) # or 'source' : { 'incl': '**/*.cpp' } # or (shortest record with the same result) 'source' : '**/*.cpp' # get all *.c and *.cpp files in the 'startdir' recursively 'source' : { 'incl': ['**/*.c', '**/*.cpp'] } # or (shorter record with the same result) 'source' : ['**/*.c', '**/*.cpp'] # get all *.cpp files in the 'startdir'/mylib recursively 'source' : 'mylib/**/*.cpp' # get all *.cpp files in the 'startdir'/src recursively # but don't include files according pattern 'src/extra*' 'source' : dict( incl = 'src/**/*.cpp', excl = 'src/extra*' ) # get all *.c files in the 'src' and in '../others' recursively 'source' : [ 'src/**/*.c', { 'incl': '**/*.c', 'startdir' : '../others' }, ] # pattern with space: 'source' : '"my prog/**/*.c"' # two file paths with spaces 'source' : '"my shlib/my util.c" "my shlib/my util2.c"'It’s possible to use selectable parameters to set this parameter.
includes¶
Include paths are used by the C/C++/D/Fortran compilers for finding headers/files. Paths should be relative to startdir or absolute. But last variant is not recommended.
If paths contain spaces and all these paths are listed in one string then each such a path must be in quotes.
Examples in YAML format:
includes : myinclude includes : include myinclude includes : [ include, myinclude ]It’s possible to use selectable parameters to set this parameter.
This parameter can be exported.
toolchain¶
Name of toolchain/compiler to use in the task. It can be any system compiler that is supported by ZenMake or a toolchain from custom toolchains. There are also the special names for autodetecting in format
auto-*
where*
is a ‘lang’ feature for programming language, for exampleauto-c
,auto-c++
, etc.Known names for C:auto-c
,gcc
,clang
,msvc
,icc
,xlc
,suncc
,irixcc
.Known names for C++:auto-c++
,g++
,clang++
,msvc
,icpc
,xlc++
,sunc++
.Known names for D:auto-d
,ldc2
,gdc
,dmd
.Known names for Fortran:auto-fc
,gfortran
,ifort
.Known names for Assembler:auto-asm
,gas
,nasm
.Note
If you don’t set
toolchain
then ZenMake will try to setauto-*
itself according values in features.In some rare cases this parameter can contain more than one value as a string with values separated by space or as list. For example, for case when C and Assembler files are used in one task, it can be
"gcc gas"
.If toolchain from custom toolchains or some system toolchain contain spaces in their names and all these toolchains are listed in one string then each such a toolchain must be in quotes.
It’s possible to use selectable parameters to set this parameter.
cflags¶
One or more compiler flags for C.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
cxxflags¶
One or more compiler flags for C++.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
dflags¶
One or more compiler flags for D.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
fcflags¶
One or more compiler flags for Fortran.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
asflags¶
One or more compiler flags for Assembler.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
cppflags¶
One or more compiler flags added at the end of compilation commands for C/C++.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
linkflags¶
One or more linker flags for C/C++/D/Fortran.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
ldflags¶
One or more linker flags for C/C++/D/Fortran at the end of the link command.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
aslinkflags¶
One or more linker flags for Assembler.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
arflags¶
Flags to give the archive-maintaining program.
It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
defines¶
One or more defines for C/C++/Assembler/Fortran.
Examples in YAML format:
defines : MYDEFINE defines : [ ABC=1, DOIT ] defines : - ABC=1 - DOIT defines : 'ABC=1 DOIT AAA="some long string"'Examples in Python format:
'defines' : 'MYDEFINE' 'defines' : ['ABC=1', 'DOIT'] 'defines' : 'ABC=1 DOIT AAA="some long string"'It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
use¶
This attribute enables the link against libraries (static or shared). It can be used for local libraries from other tasks or to declare dependencies between build tasks. Also it can be used to declare using of external dependencies. For external dependencies the format of any dependency in
use
must be:dependency-name:target-reference-name
.It can contain one or more the other task names.
If a task name contain spaces and all these names are listed in one string then each such a name must be in quotes.
Examples in YAML format:
use : util use : util mylib use : [util, mylib] use : 'util "my lib"' use : ['util', 'my lib'] use : util mylib someproject:somelibExamples in Python format:
'use' : 'util' 'use' : 'util mylib' 'use' : ['util', 'mylib'] 'use' : 'util "my lib"' 'use' : ['util', 'my lib'] 'use' : 'util mylib someproject:somelib'It can be used to specify libraries of qt5 as well. More details are here.
It’s possible to use selectable parameters to set this parameter.
libs¶
One or more names of existing shared libraries as dependencies, without prefix or extension. Usually it’s used to set system libraries.
If you use this parameter to specify non-system shared libraries for some task you may need to specify the same libraries for all other tasks which depend on the current task. For example, you set library ‘mylib’ to the task A but the task B has parameter
use
with ‘A’, then it’s recommended to add ‘mylib’ to the parameterlibs
for the task B. Otherwise you can get link error... undefined reference to ...
or something like that. Some other ways to solve this problem include using environment variableLD_LIBRARY_PATH
or changing of /etc/ld.so.conf file. But usually last method is not recommended.Example in YAML format:
libs : m rtExample in Python format:
'libs' : 'm rt'It’s possible to use selectable parameters to set this parameter.
libpath¶
One or more additional paths to find libraries. Usually you don’t need to set it.
If paths contain spaces and all these paths are listed in one string then each such a path must be in quotes.
Paths should be absolute or relative to startdir.
Examples in YAML format:
libpath : /local/lib libpath : '/local/lib "my path"' # in case of spaces in a pathExamples in Python format:
'libpath' : '/local/lib' 'libpath' : '/local/lib "my path"' # in case of spaces in a pathIt’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
monitlibs¶
One or more names from
libs
to monitor changes.For example, a project has used some system library ‘superlib’ and once this library was upgraded by a system package manager. After that the building of the project will not make a relink with the new version of ‘superlib’ if no changes in the project which can trigger such a relink. Usually it is not a problem because a project is changed much more frequently than upgrading of system libraries during development.
Any names not from
libs
are ignored.It can be True or False as well. If it is True then value of
libs
is used. If it is False then it means an empty list.By default it’s False.
Using of this parameter can slow down a building of some projects with a lot of values in this parameter. ZenMake uses sha1/md5 hashes to check changes of every library file.
It’s possible to use selectable parameters to set this parameter.
stlibs¶
The same as
libs
but for static libraries.It’s possible to use selectable parameters to set this parameter.
stlibpath¶
The same as
libpath
but for static libraries.It’s possible to use selectable parameters to set this parameter.
Also this parameter can be exported.
monitstlibs¶
The same as
monitlibs
but for static libraries. It means it’s affected by parameterstlibs
.It’s possible to use selectable parameters variables to set this parameter.
moc¶
One or more header files (.h) with C++ class declarations with Q_OBJECT. These files are handled with Qt Meta-Object Compiler, moc. Format for this parameter is the same as for the source parameter.
You can specify header files without Q_OBJECT here because ZenMake filters such files by itself. So you can specify just all .h files of your directory with header files if you wish.
It can be used only for tasks with
qt5
in features.
rclangprefix¶
Value of
qresource prefix
in generated .qrc file for a qt5 task. When .ts files are specified in the source parameter ZenMake compiles these files into .qm files. If you set therclangprefix
parameter ZenMake will insert all compiled .qm files in .qrc file to embed .qm files as internal binary resourses inside compiled task target file. And the value of this parameter can be used in the QTranslator::load method in the ‘directory’ argument in your Qt5 code.The bld-langprefix, unique-qmpaths and install-langdir parameters are ignored if the
rclangprefix
is set.It can be used only for tasks with
qt5
in features.
langdir-defname¶
Name of a define to set for your Qt5 code to detect current directory with compiled .qm files to use in the QTranslator::load method. When .ts files are specified in the source parameter ZenMake compiles these files into .qm files. But when you use the
install
command ZenMake copies these files from build directory into install directory. So during regular building and for installed application the directory with .qm files are different. Value of the define with the name from thelangdir-defname
is the install directory of .qm files for theinstall
command and the build directory of .qm files in other cases.This parameter is ignored if rclangprefix is set.
It can be used only for tasks with
qt5
in features.
bld-langprefix¶
Set build directory path prefix for compiled .qm files. It is relative to buildtypedir and defaults to
@translations
. Usually you don’t need to use this parameter.This parameter is ignored if rclangprefix is set.
It can be used only for tasks with
qt5
in features.
unique-qmpaths¶
Make unique file paths for compiled .qm files by adding name of current buld task by the pattern:
$(buildtypedir)/<bld-langprefix>/<task name>_<original .qm file name>
where buildtypedir is the built-in variable. Usually you don’t need to use this parameter.This parameter is ignored if rclangprefix is set.
It can be used only for tasks with
qt5
in features.
rpath¶
One or more paths to hard-code into the binary during linking time. It’s ignored on platforms that do not support it.
If paths contain spaces and all these paths are listed in one string then each such a path must be in quotes.
It’s possible to use selectable parameters to set this parameter.
ver-num¶
Enforce version numbering on shared libraries. It can be used with *shlib
features
for example. It can be ignored on platforms that do not support it.It’s possible to use selectable parameters to set this parameter.
run¶
A dict with parameters to run something in the task. It’ used with task features
runcmd
andtest
. It can be also just a string or a python function (for buildconf.py only). In this case it’s the same as using dict with one parametercmd
.
cmd: Command line to run. It can be any suitable command line. For convenience special built-in substitution variables
src
andtgt
can be used here. Thetgt
variable contains string with the absolute path to resulting target file of the current task. And thesrc
contains string with all source files of the task.Environment variables also can be used here but see bash-like substitutions.
For python variant of buildconf it can be python function as well. In this case such a function gets one argument as a python dict with parameters:
taskname: Name of current build task startdir: Current startdir buildroot: Root directory for building buildtype: Current buildtype target: Absolute path to resulting target. It may not be existing. waftask: Object of Waf class Task. It’s for advanced use. cwd: Working directory where to run
cmd
. By default it’s build directory for current buildtype. Path can be absolute or relative to the startdir.env: Environment variables for
cmd
. It’s adict
where each key is a name of variable and value is a value of env variable.timeout: Timeout for
cmd
in seconds. It works only when ZenMake is run with python 3. By default there is no timeout.shell: If shell is True, the specified command will be executed through the shell. By default to avoid some common problems it is True. But in many cases it’s safe to set False. In this case it avoids some overhead of using shell. In some cases it can be set to True by ZenMake/Waf even though you set it to False.
repeat: Just amount of running of
cmd
. It’s mostly for tests. By default it’s 1.If current task has parameter
run
with emptyfeatures
or with onlyruncmd
in thefeatures
then it is standalone runcmd task.If current task is not standalone runcmd task then command from parameter
run
will be run after compilation and linking. If you want to have a command that will be called before compilation and linking you can make another standalone runcmd task and specify this new task in the parameteruse
of the current task.By default ZenMake expects that any build task produces a target file and if it doesn’t find this file when the task is finished it will throw an error. And it is true for standalone runcmd tasks also. If you want to create standalone runcmd task which doesn’t produce target file you can set task parameter target to an empty string.
Examples in YAML format:
echo: run: "echo 'say hello'" target: '' test.py: run: cmd : python tests/test.py cwd : . env : { JUST_ENV_VAR: qwerty } shell : false target: '' configure : - do: find-program names: python shlib-test: features : cxxprogram test # ... run: cmd : '$(tgt) a b c' env : { ENV_VAR1: '111', ENV_VAR2: 'false' } repeat : 2 timeout : 10 # in seconds shell : false foo.luac: source : foo.lua configure : [ { do: find-program, names: luac } ] run: '${LUAC} -s -o $(tgt) $(src)'Examples in Python format:
'echo' : { 'run' : "echo 'say hello'", 'target': '', }, 'test.py' : { 'run' : { 'cmd' : 'python tests/test.py', 'cwd' : '.', 'env' : { 'JUST_ENV_VAR' : 'qwerty', }, 'shell' : False, }, 'target': '', 'configure' : [ dict(do = 'find-program', names = 'python'), ] }, 'shlib-test' : { 'features' : 'cxxprogram test', # ... 'run' : { 'cmd' : '$(tgt) a b c', 'env' : { 'ENV_VAR1' : '111', 'ENV_VAR2' : 'false'}, 'repeat' : 2, 'timeout' : 10, # in seconds, Python 3 only 'shell' : False, }, }, 'foo.luac' : { 'source' : 'foo.lua', 'configure' : [ dict(do = 'find-program', names = 'luac'), ], 'run': '${LUAC} -s -o $(tgt) $(src)', },It’s possible to use selectable parameters to set this parameter.
configure¶
A list of configuration actions (configuration checks and others). Details are here. These actions are called on configure step (in command configure).
It’s possible to use selectable parameters to set this parameter.
Results of these configuration actions can be exported with the name config-results.
export-<param> / export¶
Some task parameters can be exported to all dependent build tasks.
There two forms:
export-<param>
andexport
.In first form
<param>
is the name of the exported task parameter. The boolean True/False value or specific valid value to the<param>
can be used to export. If value is True then ZenMake exports the value of the parameter from current task to all dependent build tasks. If value is False then ZenMake exports nothing.Supported names:
includes
,defines
,config-results
,libpath
,stlibpath
,moc
and all*flags
.But the parameter
export-config-results
accepts boolean True/False only value.In second form it must be string or list with the names of parameters to export. Second form is simplified form of the first form when all values are True. And this form cannot be used to set specific value to export.
Note
By default ZenMake exports nothing (all values are False).
Exporting values are inserted in the beginning of the current parameter values in dependent tasks. It was made to have ability to overwrite parent values. For example, task A has
defines
with valueAAA=q
and task B depends on task A and hasdefines
with valueBBB=v
. So if task A hasexport-defines
with True, then actual value ofdefines
in task B will beAAA=q BBB=v
.Examples in YAML format:
# export all includes from current task export-includes: true # the same result: export: includes # export all includes and defines from current task export-includes: true export-defines: true # the same result: export: includes defines # export specific includes, value of parameter 'includes' from current # task is not used export-includes: incl1 incl2 # export specific defines, value of parameter 'defines' from current # task is not used export-defines : 'ABC=1 DOIT AAA="some long string"' # export results of all configuration actions from current task export-config-results: true # export all includes, defines and results of configuration actions export: includes defines config-resultsSpecific remarks:
includes: If specified paths contain spaces and all these paths are listed in one string then each such a path must be in quotes. defines: Defines from configuration actions are not exported. Use export-config-results
orexport
withconfig-results
for that.It’s possible to use selectable parameters (in strings) to set this parameter.
install-path¶
String representing the installation directory for the task target file. It is used in the
install
anduninstall
commands. This path must be absolute. To disable installation, set it to False or to empty string. If it’s absent then built-inprefix
,bindir
andlibdir
variables will be used to detect path. You can use any built-in substitution variable includingprefix
,bindir
andlibdir
here like this:Example in YAML format:
install-path : '$(prefix)/bin'This parameter is false for standalone runcmd tasks by default.
It’s possible to use selectable parameters to set this parameter.
install-files¶
A list of additional files to install. Each item in this list must be a dict with following parameters:
do: It is what to do and it can be
copy
,copy-as
orsymlink
. Thecopy
value means copying specified files to a directory from thedst
. Thecopy-as
value means copying one specified file to a path from thedst
so you can use a difference file name. Thesymlink
value means creation of symlink. It’s for POSIX platforms only and does nothing on MS Windows.You may not set this parameter in some cases. If this parameter is absent:
- It’s
symlink
if parametersymlink
exists in current dict.- It’s
copy
in other cases.src: If
do
iscopy
then rules for this parameter are the same as for source but with one addition: you can specify one or more paths to directory if you don’t use any ant pattern. In this case all files from specified directory will be copied recursively with directories hierarchy.If
do
iscopy-as
, it must be one path to a file. And it must be relative to the startdir or an absolute path.If
do
issymlink
, it must be one path to a file. Created symbolic link will point to this path. Also it must be relative to the startdir or an absolute path.dst: If
do
iscopy
then it must be a path to a directory. Ifdo
iscopy-as
, it must be one path to a file. Ifdo
issymlink
, this parameter cannot be used. See parametersymlink
.It must be relative to the startdir or an absolute path.
Any path here will have value of
destdir
at the beginning if thisdestdir
is set to non-empty value. Thisdestdir
can be set from command line argument--destdir
or from environment variableDESTDIR
and it is not set by default.symlink: It is like
dst
forcopy-as
but for creating a symlink. This parameter can be used only ifdo
issymlink
.It must be relative to the startdir or an absolute path.
chmod: Change file mode bits. It’s for POSIX platforms only and does nothing on MS Windows. And it cannot be used for
do
=symlink
.It must be integer or string. If it is an integer it must be correct value for python function os.chmod. For example: 0o755.
If it is a string then value will be converted to integer as octal representation of an integer. For example, ‘755’ will be converted to 493 (it’s 755 in octal representation).
By default it is 0o644.
user: Change file owner. It’s for POSIX platforms only and does nothing on MS Windows. It must be a name of existing user. It is not set by default and the value from original file will be used.
group: Change file user’s group. It’s for POSIX platforms only and does nothing on MS Windows. It must be a name of existing user’s group. It is not set by default and the value from original file will be used.
follow-symlinks: Follow symlinks from
src
ifdo
iscopy
orcopy-as
. If it is false, symbolic links in the paths fromsrc
are represented as symbolic links in thedst
, but the metadata of the original links is NOT copied; if true or omitted, the contents and metadata of the linked files are copied to the new destination.It’s true by default.
relative: This parameter can be used only if
do
issymlink
. If it is true, relative symlink will created.It’s false by default.
Some examples can be found in the directory ‘mixed/01-cshlib-cxxprogram’ in the repository here.
It’s possible to use selectable parameters to set this parameter.
install-langdir¶
Installation directory for .qm files. It defaults to
$(appdatadir)/translations
where appdatadir is the built-in variable.This parameter is ignored if rclangprefix is set.
It can be used only for tasks with
qt5
in features.
normalize-target-name¶
Convert
target
name to ensure the name is suitable for file name and has not any potential problems. It replaces all space symbols for example. Experimental. By default it is False.It’s possible to use selectable parameters to set this parameter.
enabled¶
If it’s False then current task will not be used at all. By default it is True.
It makes sense mostly to use with selectable parameters or with byfilter. With this parameter you can make a build task which can be used, for example, on Linux only or for specific toolchain or with another condition.
group-dependent-tasks¶
Although runtime jobs for the tasks may be executed in parallel, some preparation is made before this in one thread. It includes, for example, analyzing of the task dependencies and file paths in source. Such list of tasks is called build group and, by default, it’s only one build group for each project which uses ZenMake. If this parameter is true, ZenMake creates a new build group for all other dependent tasks and preparation for these dependent tasks will be run only when all jobs for current task, including all dependencies, are done.
For example, if some task produces source files (*.c, *.cpp, etc) that don’t exist at the time of such a preparation, you can get a problem because ZenMake cannot find not existing files. It is not a problem if such a file is declared in target and then this file is specified without use of ant pattern in
source
of dependent tasks. In other cases you can solve the problem by setting this parameter to True for a task which produces these source files.By default it is False. Don’t set it to True without reasons because it can slow building down.
objfile-index¶
Counter for the object file extension. By default it’s calculated automatically as unique index number for each build task.
If you set this for one task but not for others in the same project and your index number is matched with one of automatic generated indexes then it can cause compilation errors if different tasks use the same files in parameter
source
.Also you can set the same value for the all build tasks and often it’s not a problem while different tasks use the different files in parameter
source
.Set this parameter only if you know what you do.
It’s possible to use selectable parameters to set this parameter.
Note
More examples of buildconf files can be found in repository here.
Build config: selectable parameters¶
ZenMake provides ability to select values for parameters in task params depending on some conditions. This feature of ZenMake is similar to Configurable attributes from Bazel build system and main idea was borrowed from that system. But implementation is different.
It can be used for selecting different source files, includes, compiler flags and others on different platforms, different toolchains, etc.
Example in YAML format:
tasks:
# ...
conditions:
windows-msvc:
platform: windows
toolchain: msvc
buildtypes:
debug: {}
release:
cxxflags.select:
windows-msvc: /O2
default: -O2
Example in Python format:
tasks = {
# ...
}
conditions = {
'windows-msvc' : {
'platform' : 'windows',
'toolchain' : 'msvc',
},
}
buildtypes = {
'debug' : {
},
'release' : {
'cxxflags.select' : {
'windows-msvc': '/O2',
'default': '-O2',
},
},
}
In this example for build type ‘release’ we set value ‘/O2’ to ‘cxxflags’ if toolchain ‘msvc’ is used on MS Windows and set ‘-02’ for all other cases.
This method can be used for any parameter in task params excluding features in the form:
YAML format:
<parameter name>.select:
<condition name1>: <value>
<condition name2>: <value>
...
default: <value>
Python format:
'<parameter name>.select' : {
'<condition name1>' : <value>,
'<condition name2>' : <value>,
...
'default' : <value>,
}
A <parameter name> here is a parameter from task params. Examples: ‘toolchain.select’, ‘source.select’, ‘use.select’, etc.
Each condition name must refer to a key in conditions
or to one of built-in conditions (see below).
There is also special optional key default
wich means default value if none
of the conditions has been selected. If the key default
doen’t exist then ZenMake
tries to use the value of <parameter name> if it exists. If none of the
conditions has been selected and no default value for the parameter then this
parameter will not be used.
Keys in conditions are just strings which consist of latin characters, digits and symbols ‘+’, ‘-’, ‘_’ . A value for each condition is a dict with one or more such parameters:
platform: Selected platform like ‘linux’, ‘windows’, ‘darwin’, etc. Valid values are the same as for
default
in the buildtypes.It can be one value or list of values or string with more than one value separated by spaces like this: ‘linux windows’.
host-os: Selected basic name of a host operating system. It is almost the same as the
platform
parameter but for the MSYS2 and cygwin platforms it is always ‘windows’ and for the darwin platform it is ‘macos’.distro: Name of a Linux distribution like ‘debian’, ‘fedora’, etc. This name is empty string for other operating systems.
cpu-arch: Selected current CPU architecture. Actual it’s a result of the python function platform.machine() See https://docs.python.org/library/platform.html. Some possible values are: arm, i386, i686, x86_64, AMD64. Real value depends on a platform. For example, on Windows you can get AMD64 while on Linux you gets x86_64 on the same host.
Current value can be obtained also with the command
zenmake sysinfo
.It can be one value or list of values or string with more than one value separated by spaces like this: ‘i686 x86_64’.
toolchain: Selected/detected toolchain.
It can be one value or list of values or string with more than one value separated by spaces like this: ‘gcc clang’.
task: Selected build task name.
It can be one value or list of values or string with more than one value separated by spaces like this: ‘mylib myprogram’.
buildtype: Selected buildtype.
It can be one value or list of values or string with more than one value separated by spaces like this: ‘debug release’.
env: Check system environment variables. It’s a dict of pairs <variable> : <value>.
Example in YAML format:
conditions: my-env: env: TEST: 'true' # use 'true' as a string CXX: gccExample in Python format:
conditions = { 'my-env' : { 'env' : { 'TEST' : 'true', 'CXX' : 'gcc', } }, }
If a parameter in a condition contains more than one value then any of these
values will fulfill selected condition. It means if some condition, for example,
has platform
which contains 'linux windows'
without other parameters then
this condition will be selected on any of these platforms (on GNU/Linux and
on MS Windows). But with parameter env
the situation is different. This
parameter can contain more than one environment variable and a condition will be
selected only when all of these variables are equal to existing variables from the
system environment. If you want to have condition to select by any of such
variables you can do it by making different conditions in
conditions.
Note
There is a constraint for toolchain.select
- it’s not possible to use
a condition with the ‘toolchain’ parameter inside toolchain.select
.
Only one record from *.select
for each parameter can be selected for each task
during configuring but a condition name in *.select
can be string with more than
one name from conditions
. Such names can be used with ‘and’, ‘or’, ‘not’
and ‘()’ to form different conditions in *.select
.
Example in YAML format:
conditions:
linux:
platform: linux
g++:
toolchain: g++
buildtypes:
debug: {}
release:
cxxflags.select:
# will be selected only on linux with selected/detected toolchain g++
linux and g++: -Ofast
# will be selected in all other cases
default: -O2
Example in Python format:
conditions = {
'linux' : {
'platform' : 'linux',
},
'g++' : {
'toolchain' : 'g++',
},
}
buildtypes = {
'debug' : {
},
'release' : {
'cxxflags.select' : {
# will be selected only on linux with selected/detected toolchain g++
'linux and g++': '-Ofast',
# will be selected in all other cases
'default': '-O2',
},
},
}
For convenience there are ready to use built-in conditions for known platforms and
supported toolchains. So in example above the conditions
variable is not needed
at all because conditions with names linux
and g++
already exist:
in YAML format:
# no declaration of conditions
buildtypes:
debug: {}
release:
cxxflags.select:
# will be selected only on linux with selected/detected toolchain g++
linux and g++: -Ofast
# will be selected in all other cases
default: -O2
in Python format:
# no declaration of conditions
buildtypes = {
'debug' : {
},
'release' : {
'cxxflags.select' : {
# will be selected only on linux with selected/detected toolchain g++
'linux and g++': '-Ofast',
# will be selected in all other cases
'default': '-O2',
},
},
}
Also you can use built-in conditions for supported buildtypes. But if any name of supported buildtype is the same as one of known platforms or supported toolchains then such a buildtype cannot be used as a built-in condition. For example, you may want to make/use the buildtype ‘linux’ and it will be possible but you have to declare a different name to use it in conditions in this case because the ‘linux’ value is one of known platforms.
There is one detail about built-in conditions for toolchains - only toolchains
supported for current build tasks can be used. ZenMake detects them from
all features
of all existing build tasks in current project during configuring.
For example, if tasks exist for C language only then supported toolchains for
all other languages cannot be used as a built-in condition.
If you declare condition in conditions with the same name of a built-in condition then your condition will be used instead of that built-in condition.
Build config: edeps¶
The config parameter edeps
is a dict with
configurations of external non-system dependencies.
General description of external dependencies is here.
Each such a dependency can have own unique name and parameters:
rootdir¶
A path to the root of the dependency project. It should be path to directory with the build script of the dependency project. This path can be relative to the startdir or absolute.
targets¶
A dict with descriptions of targets of the dependency project. Each target has a reference name which can be in use in format
dependency-name:target-reference-name
and parameters:
dir: A path with the current target file. Usually it’s some build directory. This path can be relative to the startdir or absolute.
type: It’s type of the target file. This type has effects to the link of the build tasks and some other things. Supported types:
stlib: The target file is a static library. shlib: The target file is a shared library. program: The target file is an executable file. file: The target file is any file. name: It is a base name of the target file which is used for detecting of resulting target file name depending on destination operating system, selected toolchain, value of
type
, etc.If it’s not set the target reference name is used.
ver-num: It’s a version number for the target file if it is a shared library. It can have effect on resulting target file name.
fname: It’s a real file name of the target. Usually it’s detected by ZenMake from other parameters but you can set it manually but it’s not recommended until you really need it. If parameter
type
is equal tofile
the value of this parameter is always equal to value of parametername
by default.Example in YAML format for non-ZenMake dependency:
targets: # 'shared-lib' and 'static-lib' are target reference names shared-lib: dir : ../foo-lib/_build_/debug type: shlib name: fooutil static-lib: dir : ../foo-lib/_build_/debug type: stlib name: fooutilExample in Python format for non-ZenMake dependency:
'targets': { # 'shared-lib' and 'static-lib' are target reference names 'shared-lib' : { 'dir' : '../foo-lib/_build_/debug', 'type': 'shlib', 'name': 'fooutil', }, 'static-lib' : { 'dir' : '../foo-lib/_build_/debug', 'type': 'stlib', 'name': 'fooutil', }, },
export-includes¶
A list of paths with ‘includes’ for C/C++/D/Fortran compilers to export from the dependency project for all build tasks which depend on the current dependency. Paths should be relative to the startdir or absolute but last variant is not recommended.
If paths contain spaces and all these paths are listed in one string then each such a path must be in quotes.
rules¶
A dict with descriptions of rules to produce targets files of dependency. Each rule has own reserved name and parameters to run. The rule names that allowed to use are:
configure
,build
,test
,clean
,install
,uninstall
.The parameters for each rule can be a string with a command line to run or a dict with attributes:
cmd: A command line to run. It can be any suitable command line.
cwd: A working directory where to run
cmd
. By default it’s the rootdir. This path can be relative to the startdir or absolute.env: Environment variables for
cmd
. It’s adict
where each key is a name of variable and value is a value of env variable.timeout: A timeout for
cmd
in seconds. By default there is no timeout.shell: If shell is True, the specified command will be executed through the shell. By default it is False. In some cases it can be set to True by ZenMake even though you set it to False.
trigger: A dict that describes conditions to run the rule. If any configured trigger returns True then the rule will be run. You can configure one or more triggers for each rule. ZenMake supports the following types of trigger:
always: If it’s True then the rule will be run always. If it’s False and no other triggers then the rule will not be run automatically.
paths-exist: This trigger returns True only if configured paths exist on a file system. You can set paths as a string, list of strings or as a dict like for config task parameter source.
Examples in YAML format:
trigger: paths-exist: /etc/fstab trigger: paths-exist: [ /etc/fstab, /tmp/somefile ] trigger: paths-exist: startdir: '../foo-lib' incl: '**/*.label'Examples in Python format:
'trigger': { 'paths-exist' : '/etc/fstab', } 'trigger': { 'paths-exist' : ['/etc/fstab', '/tmp/somefile'], } 'trigger': { 'paths-exist' : dict( startdir = ../foo-lib, incl = '**/*.label', ), }paths-dont-exist: This trigger is the same as
paths-exist
but returns True if configured paths don’t exist.env: This trigger returns True only if all configured environment variables exist and equal to configured values. Format is simple: it’s a
dict
where each key is a name of variable and value is a value of environment variable.no-targets: If it is True this trigger returns True only if any of target files for current dependency doesn’t exist. It can be useful to detect the need to run ‘build’ rule. This trigger cannot be used in ZenMake command ‘configure’.
func: This trigger is a custom python function that must return True or False. This function gets the following parameters as arguments:
zmcmd: It’s a name of the current ZenMake command that has been used to run the rule. targets: A list of configured/detected targets. It’s can be None if rule has been run from command ‘configure’. It’s better to use **kwargs in this function because some new parameters can be added in the future.
This trigger cannot be used in YAML buildconf file.
Note
For any non-ZenMake dependency there are following default triggers for rules:
configure: { always: true }
build: { no-targets: true }
Any other rule: { always: false }
Note
You can use command line option
-E
/--force-edeps
to run rules for external dependencies without checking triggers.zm-commands: A list with names of ZenMake commands in which selected rule will be run. By default each rule can be run in the ZenMake command with the same name only. For example, rule ‘configure’ by default can be run with the command ‘configure’ and rule ‘build’ with the command ‘build’, etc. But here you can set up a different behavior.
buildtypes-map¶
This parameter is used only for external dependencies which are other ZenMake projects. By default ZenMake uses value of current
buildtype
for all such dependencies to run rules but in some cases names of buildtype can be not matched. For example, current project can have buildtypesdebug
andrelease
but project from dependency can have buildtypesdbg
andrls
. In this case you can use this parameter to set up the map of these buildtype names.Example in YAML format:
buildtypes-map: debug : dbg release : rlsExample in Python format:
buildtypes-map: { 'debug' : 'dbg', 'release' : 'rls', }
Some examples can be found in the directory ‘external-deps’ in the repository here.
Build config: extended syntax¶
For convenience, ZenMake supports some syntax extensions in buildconf files.
Syntactic sugar¶
There are some syntactic sugar constructions that can be used to make a buildconf a little shorter.
configure¶
It can be used as a replacement for configure task param.
For example you have (in YAML format):
tasks: util: features : cshlib source : shlib/**/*.c configure: - do: check-headers names : stdio.h test: features : cprogram source : prog/**/*.c use : util configure: - do: check-headers names : stdio.hSo it can be converting into this:
tasks: util: features : cshlib source : shlib/**/*.c test: features : cprogram source : prog/**/*.c use : util configure: - do: check-headers names : stdio.hThe
configure
above is the same as following construction:byfilter: - for: all set: configure: - do: check-headers names : stdio.hIn addition to regular arguments for configure task param you can use
for
/not-for
/if
in the same way as in the byfilter.Example:
tasks: # .. skipped configure: - do: check-headers names : stdio.h not-for: { task: mytask }
install¶
Like as previous
configure
this can be used as a replacement for install-files task param.Example:
tasks: # .. skipped install: - for: { task: gui } src: 'some/src/path/ui.res' dst: '$(prefix)/share/$(prjname)'
Substitutions¶
There are two types of substitutions in ZenMake: bash-like variables with ability to use system environment variables and built-in variables.
Bash-like variables¶
ZenMake supports substitution variables with syntax similar to syntax of bash variables.
Both $VAR and ${VAR} syntax are supported. These variables can be used in any buildconf parameter value of string/text type.
in YAML format:
param: '${VAR}/some-string'in Python format:
'param' : '${VAR}/some-string'
ZenMake looks such variables in environment variables at first and then in the buildconf file. You can use a $$ (double-dollar sign) to prevent use of environment variables.
Example in YAML format:
# set 'fragment' variable
fragment: |
program
end program
# set 'GCC_BASE_FLAGS' variable
GCC_BASE_FLAGS: -std=f2018 -Wall
tasks:
# ... skipped values
test:
features : fcprogram
source : src/calculator.f90 src/main.f90
includes : src/inc
use : staticlib sharedlib
configure:
- do: check-code
text: $$fragment # <-- substitution without env
label: fragment
buildtypes:
# GCC_BASE_FLAGS can be overwritten by environment variable with the same name
debug : { fcflags: $GCC_BASE_FLAGS -O0 }
release: { fcflags: $GCC_BASE_FLAGS -O2 }
default: debug
Note
These substitution variables inherit values from parent buildconf in subdirs.
Also values for such variables can be set by some configuration actions.
For example see var
in configuration action find-program
. But in this case
these values are not visible everywhere.
For YAML format there are some constraints with ${VAR} form due to YAML specification:
debug : { fcflags: $GCC_BASE_FLAGS -O0 } # works
debug : { fcflags: "$GCC_BASE_FLAGS -O0" } # works
debug : { fcflags: ${GCC_BASE_FLAGS} -O0 } # doesn't work
debug : { fcflags: "${GCC_BASE_FLAGS} -O0" } # works
debug :
fcflags: ${GCC_BASE_FLAGS} -O0 # works
Built-in variables¶
ZenMake has some built-in variables that can be used as substitutions. To avoid possible conflicts with environment and bash-like variables the syntax of substitutions is a little bit different in this case:
in YAML format:
param: '$(var)/some-string'in Python format:
'param' : '$(var)/some-string'
List of built-in variables:
- prjname
- Name of the current project. It can be changed via
name
from here.
- topdir
- Absolute path of startdir of the top-level buildconf file. Usually it is root directory of the current project.
- buildrootdir
- Absolute path of buildroot.
- buildtypedir
- Absolute path of current buildtype directory. It is current value of buildroot plus current buildtype.
- prefix
- The installation prefix. It is a directory that is prepended onto all install directories and it defaults to
/usr/local
on UNIX andC:/Program Files/$(prjname)
on Windows. It can be changed via environment variable PREFIX or via--prefix
on the command line.
- execprefix
- The installation prefix for machine-specific files. In most cases it is the same as the
$(prefix)
variable. It was introduced mostly for compatibility with GNU standard: https://www.gnu.org/prep/standards/html_node/Directory-Variables.html. It can be changed via environment variable EXEC_PREFIX or via--execprefix
on the command line.
- bindir
- The directory for installing executable programs that users can run. It defaults to
$(exeprefix)/bin
on UNIX and$(exeprefix)
on Windows. It can be changed via environment variable BINDIR or via--bindir
on the command line.
- sbindir
- The directory for installing executable programs that can be run, but are only generally useful to system administrators. It defaults to
$(exeprefix)/sbin
on UNIX and$(exeprefix)
on Windows. It can be changed via environment variable SBINDIR or via--sbindir
on the command line.
- libexecdir
- The directory for installing executable programs to be run by other programs rather than by users. It defaults to
$(exeprefix)/libexec
on UNIX and$(exeprefix)
on Windows. It can be changed via environment variable LIBEXECDIR or via--libexecdir
on the command line.
- libdir
- The installation directory for object files and libraries of object code. It defaults to
$(exeprefix)/lib
or$(exeprefix)/lib64
on UNIX and$(exeprefix)
on Windows. On Debian/Ubuntu, it may be$(exeprefix)/lib/<multiarch-tuple>
. It can be changed via environment variable LIBDIR or via--libdir
on the command line.
- sysconfdir
- The installation directory for read-only single-machine data. It defaults to
$(prefix)/etc
on UNIX and$(prefix)
on Windows. It can be changed via environment variable SYSCONFDIR or via--sysconfdir
on the command line.
- sharedstatedir
- The installation directory for modifiable architecture-independent data. It defaults to
/var/lib
on UNIX and$(prefix)
on Windows. It can be changed via environment variable SHAREDSTATEDIR or via--sharedstatedir
on the command line.
- localstatedir
- The installation directory for modifiable single-machine data. It defaults to
$(prefix)/var
. It can be changed via environment variable LOCALSTATEDIR or via--localstatedir
on the command line.
- includedir
- The installation directory for C header files. It defaults to
$(prefix)/include
. It can be changed via environment variable INCLUDEDIR or via--includedir
on the command line.
- datarootdir
- The installation root directory for read-only architecture-independent data. It defaults to
$(prefix)/share
on UNIX and$(prefix)
on Windows. It can be changed via environment variable DATAROOTDIR or via--datarootdir
on the command line.
- datadir
- The installation directory for read-only architecture-independent data. It defaults to
$(datarootdir)
. It can be changed via environment variable DATADIR or via--datadir
on the command line.
- appdatadir
- The installation directory for read-only architecture-independent application data. It defaults to
$(datarootdir)/$(prjname)
on UNIX and$(datarootdir)
on Windows. It can be changed via environment variable APPDATADIR or via--appdatadir
on the command line.
- docdir
- The installation directory for documentation. It defaults to
$(datarootdir)/doc/$(prjname)
on UNIX and$(datarootdir)/doc
on Windows. It can be changed via environment variable DOCDIR or via--docdir
on the command line.
- mandir
- The installation directory for man documentation. It defaults to
$(datarootdir)/man
. It can be changed via environment variable MANDIR or via--mandir
on the command line.
- infodir
- The installation directory for info documentation. It defaults to
$(datarootdir)/info
. It can be changed via environment variable INFODIR or via--infodir
on the command line.
- localedir
- The installation directory for locale-dependent data. It defaults to
$(datarootdir)/locale
. It can be changed via environment variable LOCALEDIR or via--localedir
on the command line.
In some cases some extra variables are provided. For example,
variables src
and tgt
are provided
for the cmd
in the task parameter run.
Built-in variables cannot be used in buildconf parameters which are used to determine values of that built-in variables. These parameters are:
- startdir, buildroot, realbuildroot
- buildtypedir only: the
default
in the buildtypes- buildtypedir only: the
buildtypes
,platform
andtask
in the byfilter
Commands¶
Here are some descriptions of general commands. You can get the list of the all
commands with a short description by zenmake help
or zenmake --help
.
To get help on selected command you
can use zenmake help <selected command>
or
zenmake <selected command> --help
. Some commands have short aliases.
For example you can use bld
instead of build
and dc
instead of distclean
.
- configure
- Configure the project. In most cases you don’t need to call this command
directly. The
build
command calls this command by itself if necessary. This command processes most of values from buildconf of a project. Any change in buildconf leads to call of this command. You can change this behaviour with parameterautoconfig
in buildconf general features. - build
- Build the project in the current directory. It’s the main command. To see all
possible parameters use
zenmake help build
orzenmake build --help
. For example you can use-v
to see more info about building process or-p
to use progress bar instead of text logging. By default it calls theconfigure
command by itself if necessary. - test
- Build (if necessery) and run tests in the current directory. If the project
has no tests it’s almost the same as running the
build
command. Thetest
command builds and runs tests by default while thebuild
command doesn’t. - run
- Build the project (if necessery) and run one executable target from the build directory. You can specify build task/target to run if the project has more than one executable targets or omit it if the project has only one executable target. To provide command line args directly to your program you can put them after ‘–’ in command line after all args for ZenMake. This command is for fast checking of the built project.
- clean
- Remove build files for selected
buildtype
of the project. It doesn’t touch other build files. - cleanall
- Remove the build directory of the project with everything in it.
- install
- Install the build targets in some destination directory using installation
prefix. It builds targets by itself if necessary.
You can control paths with environment variables
or command line parameters (see
zenmake help install
). It looks like classicmake install
in common. - uninstall
- Remove the build targets installed with the
install
command.
Environment variables¶
ZenMake supports some environment variables that can be used. Most of examples
are for POSIX platforms (Linux/MacOS) with gcc
and clang
installed.
Also see bash-like substitutions.
- AR
- Set archive-maintaining program.
- CC
Set C compiler. It can be name of installed a system compiler or any path to existing compiler. It overrides values from build config if present. Example:
CC=clang zenmake build -B
- CXX
Set C++ compiler. It can be name of installed a system compiler or any path to existing compiler. It overrides values from build config if present. Example:
CXX=clang++ zenmake build -B
- DC
Set D compiler. It can be name of installed a system compiler or any path to existing compiler. It overrides values from build config if present. Example:
DC=ldc2 zenmake build -B
- FC
Set Fortran compiler. It can be name of installed a system compiler or any path to existing compiler. It overrides values from build config if present. Example:
FC=gfortran zenmake build -B
- AS
Set Assembler. It can be name of installed a system compiler or any path to existing compiler. It overrides values from build config if present. Example:
AS=gcc zenmake build -B
- ARFLAGS
- Flags to give the archive-maintaining program.
- CFLAGS
Extra flags to give to the C compiler. Example:
CFLAGS='-O3 -fPIC' zenmake build -B
- CXXFLAGS
Extra flags to give to the C++ compiler. Example:
CXXFLAGS='-O3 -fPIC' zenmake build -B
- CPPFLAGS
- Extra flags added at the end of compilation commands for C/C++.
- DFLAGS
Extra flags to give to the D compiler. Example:
DFLAGS='-O' zenmake build -B
- FCFLAGS
Extra flags to give to the Fortran compiler. Example:
FCFLAGS='-O0' zenmake build -B
- ASFLAGS
Extra flags to give to the Assembler. Example:
ASFLAGS='-Os' zenmake build -B
- LINKFLAGS
Extra list of linker flags for C/C++/D/Fortran. Example:
LINKFLAGS='-Wl,--as-needed' zenmake build -B
- LDFLAGS
Extra list of linker flags at the end of the link command for C/C++/D/Fortran. Example:
LDFLAGS='-Wl,--as-needed' zenmake build -B
- ASLINKFLAGS
Extra list of linker flags for Assembler files. Example:
ASLINKFLAGS='-s' zenmake build -B
- JOBS
Default value for the amount of parallel jobs. Has no effect when
-j
is provided on the command line. Example:JOBS=2 zenmake build
- NUMBER_OF_PROCESSORS
- Default value for the amount of parallel jobs when the JOBS environment
variable is not provided; it is usually set on windows systems. Has no
effect when
-j
is provided on the command line. - NOCOLOR
When set to a non-empty value, colors in console outputs are disabled. Has no effect when
--color
is provided on the command line. Example:NOCOLOR=1 zenmake build
- NOSYNC
When set to a non-empty value, console outputs are displayed in an asynchronous manner; console text outputs may appear faster on some platforms. Example:
NOSYNC=1 zenmake build
- BUILDROOT
A path to the root of a project build directory. The path can be absolute or relative to the current directory. See also buildroot. Example:
BUILDROOT=bld zenmake build
- DESTDIR
Default installation base directory when
--destdir
is not provided on the command line. It’s mostly for installing to a temporary directory. For example it can be used to create deb/rpm/etc packages. Example:DESTDIR=dest zenmake install
- PREFIX
Set value of built-in variable prefix as the installation prefix. This path is always considered as an absolute path or as a relative path to
DESTDIR
. Example:PREFIX=/usr zenmake install
- EXEC_PREFIX
- Set value of built-in variable execprefix as the installation prefix for machine-specific files.
- BINDIR
Set value of built-in variable bindir as the directory for installing executable programs that users can run. This path is always considered as an absolute path or as a relative path to
DESTDIR
. Example:BINDIR=/usr/bin zenmake install
- SBINDIR
- Set value of built-in variable sbindir
as the directory for installing executable programs that can be run, but are
only generally useful to system administrators.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- LIBEXECDIR
- Set value of built-in variable libexecdir
as the directory for installing executable programs to be run by other programs
rather than by users.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- LIBDIR
- Set value of built-in variable libdir
as the installation directory for object files and libraries of object code.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- SYSCONFDIR
- Set value of built-in variable sysconfdir
as the installation directory for read-only single-machine data.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- LOCALSTATEDIR
- Set value of built-in variable localstatedir
as the installation directory for modifiable single-machine data.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- INCLUDEDIR
- Set value of built-in variable includedir
as the installation directory for C header files.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- DATAROOTDIR
- Set value of built-in variable datarootdir
as the installation root directory for read-only architecture-independent data.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- DATADIR
- Set value of built-in variable datadir
as the installation directory for read-only architecture-independent data.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- APPDATADIR
- Set value of built-in variable appdatadir
as the installation directory for read-only architecture-independent application data.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- DOCDIR
- Set value of built-in variable docdir
as the installation directory for documentation.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- MANDIR
- Set value of built-in variable mandir
as the installation directory for man documentation.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- INFODIR
- Set value of built-in variable infodir
as the installation directory for info documentation.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- LOCALEDIR
- Set value of built-in variable localedir
as the installation directory for locale-dependent data.
This path is always considered as an absolute path or
as a relative path to
DESTDIR
.
- QT5_BINDIR
- Set the bin directory of the installed Qt5 toolkit. This directory must
contain such tools like qmake, moc, uic, etc.
This path must be absolute native path or path relative
to the current working directory but last variant is not recommended.
This variable can be especially useful for standalone installation of Qt5,
for example on Windows.
The
PATH
andQT5_SEARCH_ROOT
environment variables are ignored ifQT5_BINDIR
is not empty.
- QT5_LIBDIR
- Set the library directory of the installed Qt5 toolkit.
This path must be absolute native path or path relative
to the current working directory but last variant is not recommended.
Usually you don’t need to use this variable if you
set the
QT5_BINDIR
variable.
- QT5_INCLUDES
- Set the directory with ‘includes’ of the installed Qt5 toolkit.
This path must be absolute native path or path relative
to the current working directory but last variant is not recommended.
Usually you don’t need to use this variable if you
set the
QT5_BINDIR
variable. This variable has no effect on systems with pkg-config/pkgconf installed (while you don’t turn on the QT5_NO_PKGCONF).
- QT5_SEARCH_ROOT
Set the root directory to search for installed Qt5 toolkit(s). ZenMake will try to find the bin directories of all Qt5 toolkits in this directory recursively. Dot not set this variable to path like
/
orC:\
because it will slow down the detection very much. Qt5 toolkits found in this directory have priority over values from thePATH
environment variable. You can set more than one directories using path separator (;
on Windows and:
on other OS) like this:QT5_SEARCH_ROOT=/usr/local/qt:/usr/local/opt/qt zenmake
It defaults to
C:\Qt
on Windows. Usually you don’t need to use this variable on Linux.
- QT5_MIN_VER
- Set minimum version of Qt5. For example it can be
5.1
or5.1.2
.
- QT5_MAX_VER
- Set maximum version of Qt5. For example it can be
5.12
or5.12.2
.
- QT5_USE_HIGHEST_VER
- By default ZenMake will use first useful version of Qt5. When this variable set to a ‘True’, ‘true’, ‘yes’ or non-zero number then ZenMake will try to use the highest version of Qt5 among found versions.
- QT5_NO_PKGCONF
- When set to a ‘True’, ‘true’, ‘yes’ or non-zero number, ZenMake will not use pkg-config/pkgconf to configure building with Qt5. Usually you don’t need to use this variable.
- QT5_{MOC,UIC,RCC,LRELEASE,LUPDATE}
- These variables can be used to specify full file paths to Qt5 tools
moc
,uic
,rcc
,lrelease
andlupdate
. Usually you don’t need to use these variables. - ZM_CACHE_CFGACTIONS
When set to a ‘True’, ‘true’, ‘yes’ or non-zero number, ZenMake tries to use a cache for some configuration actions. Has no effect when
--cache-cfg-actions
is provided on the command line. It can speed up next runs of some configuration actions but also it can ignore changes in toolchains, system paths, etc. In general, it is safe to use it if there were no changes in the current system. Example:ZM_CACHE_CFGACTIONS=1 zenmake configure
Supported languages¶
C/C++¶
C an C++ are main languages that ZenMake supports. And the most of ZenMake features were made for these languages.
Supported compilers:
- C:
- GCC C (gcc): regularly tested
- CLANG C from LLVM (clang): regularly tested
- Microsoft Visual C/C++ (msvc): regularly tested
- Intel C/C++ (icc): should work but not tested
- IBM XL C/C++ (xlc): should work but not tested
- Oracle/Sun C (suncc): should work but not tested
- IRIX/MIPSpro C (irixcc): may be works, not tested
- C++:
- GCC C++ (g++): regularly tested
- CLANG C++ from LLVM (clang++): regularly tested
- Microsoft Visual C/C++ (msvc): regularly tested
- Intel C/C++ (icpc): should work but not tested
- IBM XL C/C++ (xlc++): should work but not tested
- Oracle/Sun C++ (sunc++): should work but not tested
Examples of projects can be found in the directory c
and cpp
in the repository here.
Assembler¶
ZenMake supports gas (GNU Assembler) and has experimental support for nasm/yasm.
Examples of projects can be found in the directory asm
in the repository here.
D¶
ZenMake supports compiling for D language. You can configure and build D code like C/C++ code but there are some limits:
- There is no support for MS Windows yet.
- There is no support for D package manager DUB.
While nobody uses ZenMake for D, there are no plans to resolve these issues.
Supported compilers:
- DMD Compiler - official D compiler (dmd): regularly tested
- GCC D Compiler (gdc): regularly tested
- LLVM D compiler (ldc2): regularly tested
Examples of projects can be found in the directory d
in the repository here.
Supported toolkits¶
Qt5¶
To build C++ project with Qt5 you can put qt5
in features.
In such tasks in the source parameter
not only .cpp files but .qrc, .ui and .ts files can be specified as well.
There are additional task parameters for Qt5 tasks: moc, rclangprefix, langdir-defname, bld-langprefix, unique-qmpaths, install-langdir.
There are also several additional environment variables for Qt5 toolkit such as: QT5_BINDIR, QT5_SEARCH_ROOT, QT5_LIBDIR and some others.
ZenMake tries to find Qt5 with qmake
and searches for it in
QT5_SEARCH_ROOT
and in the
system PATH
environment variables.
You can use QT5_BINDIR
to set directory path
with qmake
in it.
The PATH
and QT5_SEARCH_ROOT
environment variables are ignored
in this case.
You can specify minimum/maximum version of Qt5 with the QT5_MIN_VER and QT5_MAX_VER environment variables.
To specify needed Qt5 modules you should use the use parameter like this:
use : QtWidgets QtDBus # original title case of Qt5 modules must be used
ZenMake always adds QtCore
module to the use
for tasks with qt5
in features because every
other Qt5 module depends on QtCore
module.
So you don’t need to specify QtCore
to the use
parameter.
Simple Qt5 task can be like that:
tasks:
myqt5app:
features : cxxprogram qt5
source : prog/**/*.cpp prog/**/*.qrc prog/**/*.ui prog/**/*.ts
moc : prog/**/*.h
use : QtWidgets
Also it is recommended to look at examples in the qt5
directory
in the repository here.
Configuration actions¶
ZenMake supports some configuration actions. They can be used to check system
libraries, headers, etc. To set configuration actions use the configure
parameter
in task params. The value of the configure
parameter
must be a list of such actions. An item in the list
can be a dict
where do
specifies what to do, in other words it is some type of
configuration action. It’s like a function where do
describes the name of
a function and others parameters are parameters for the function.
There is another possible value for such an item in python format of buildconf file
and it is a python function which must return True/False on Success/Failure.
If such a function raises some exception then ZenMake interprets it
as if the function returns False. This function can be without arguments or
with named arguments: taskname
, buildtype
.
It’s better to use **kwargs to have universal way to work with any input arguments.
These actions can be run sequentially or in parallel (see do
= parallel
).
And they all are called on the configure step (in command configure).
Results of the same configuration actions are cached when it’s possible but not between runnings of ZenMake.
These configuration actions in dict
format:
do
=check-headers
Parameters:
names
,defname
= ‘’,defines
= [],mandatory
= True.Supported languages: C, C++.
Check existence of C/C++ headers from the
names
list.The
defname
parameter is a name of a define to set for your code when the action is over. By default the name for each header is generated in the form ‘HAVE_<HEADER NAME>=1’. For example, if you set ‘cstdio’ in thenames
then the define ‘HAVE_CSTDIO=1’ will be generated. If you set ‘stdio.h’ in thenames
then the define ‘HAVE_STDIO_H=1’ will be generated.The
defines
parameter can be used to set additional C/C++ defines to use in compiling of the action. These defines will not be set for your code, only for the action.The toolchain, includes and libpath task parameters affect this type of action.
do
=check-libs
Parameters:
names
= [],fromtask
= True,defines
= [],autodefine
= False,mandatory
= True.Supported languages: C, C++.
Check existence of the shared libraries from the
libs
task parameter or/and from thenames
list. Iffromtask
is set to False then names of libraries from thelibs
task parameter will not be used for checking. Ifautodefine
is set to True it generates C/C++ define name likeHAVE_LIB_LIBNAME=1
.The
defines
parameter can be used to set additional C/C++ defines to use in compiling of the action. These defines will not be set for your code, only for the action.The toolchain, includes and libpath task parameters affect this type of action.
do
=check-code
Parameters:
text
= ‘’,file
= ‘’,label
= ‘’,defines
= [],defname
= ‘’,execute
= False,mandatory
= True.Supported languages: C, C++, D, Fortran.
Provide piece of code for the test. Code can be provided with the
text
parameter as a plane text or with thefile
parameter as a path to the file with a code. This path can be absolute or relative to the startdir. At least one of thetext
orfile
parameters must be set.The
label
parameter can be used to mark message of the test. If theexecute
parameter is True it means that the resulting binary will be executed and the result will have effect on the current configuration action.The
defname
parameter is a name of C/C++/D/Fortran define to set for your code when the test is over. There is no such a name by default.The
defines
parameter can be used to set additional C/C++/D/Fortran defines to use in compiling of the test. These defines will not be set for your code, only for the test.The toolchain, includes and libpath task parameters affect this type of action.
do
=find-program
Parameters:
names
,paths
,var
= ‘’,mandatory
= True.Supported languages: all languages supported by ZenMake.
Find a program. The
names
parameter must be used to specify one or more possible file names for the program. Do not add an extension for portability. This action does nothing ifnames
is empty.The
paths
parameter can be used to set paths to find the program, but usually you don’t need to use it because by default thePATH
system environment variable is used. Also the Windows Registry is used on MS Windows if the program was not found.The
var
parameter can be used to set substitution variable name. By default it’s a first name from thenames
in upper case and without symbols ‘-’ and ‘.’. If this name is found in environment variables, ZenMake will use it instead of trying to find the program. Also this name can be used in parameter run like this:in YAML format:
foo.luac: source : foo.lua configure : [ { do: find-program, names: luac } ] # var 'LUAC' will be set in 'find-program' if 'luac' is found. run: '${LUAC} -s -o $(tgt) $(src)'in Python format:
'foo.luac' : { 'source' : 'foo.lua', 'configure' : [ dict(do = 'find-program', names = 'luac'), ], # var 'LUAC' will be set in 'find-program' if 'luac' is found. 'run': '${LUAC} -s -o $(tgt) $(src)', },do
=find-file
Parameters:
names
,paths
,var
= ‘’,mandatory
= True.Supported languages: all languages supported by ZenMake.
Find a file on file system. The
names
parameter must be used to specify one or more possible file names. This action does nothing ifnames
is empty.The
paths
parameter must be used to set paths to find the file. Each path can be absolute or relative to the startdir. By default it’s ‘.’ which means startdir.The
var
parameter can be used to set substitution variable name. By default it’s a first name from thenames
in upper case and without symbols ‘-’ and ‘.’.do
=call-pyfunc
Parameters:
func
,mandatory
= True.Supported languages: any but only in python format of buildconf file.
Call a python function. It’a another way to use python function as an action. In this way you can use the
mandatory
parameter.do
=pkgconfig
Parameters:
toolname
= ‘pkg-config’,toolpaths
,packages
,cflags
= True,libs
= True,static
= False,defnames
= True,def-pkg-vars
,tool-atleast-version
,pkg-version
= False,mandatory
= True.Supported languages: C, C++.
Execute
pkg-config
or compatible tool (for examplepkgconf
) and use results. Thetoolname
parameter can be used to set name of the tool and it is ‘pkg-config’ by default. Thetoolpaths
parameter can be used to set paths to find the tool, but usually you don’t need to use it.The
packages
parameter is required parameter to set one or more names of packages in database of pkg-config. Each such a package name can be used with ‘>’, ‘<’, ‘=’, ‘<=’ or ‘>=’ to check version of a package.The parameters named
cflags
(default: True),libs
= (default: True),static
(default: False) are used to set corresponding command line parameters--cflags
,--libs
,--static
for ‘pkg-config’ to get compiler/linker options. Ifcflags
orlibs
is True then obtained compiler/linker options are used by ZenMake in a build task. Parameterstatic
means forcing of static libraries and it is ignored ifcflags
andlibs
are False.The
defnames
parameter is used to set C/C++ defines. It can be True/False ordict
. If it’s True then default names for defines will be used. If it’s False then no defines will be used. If it’sdict
then keys must be names of used packages and values must be dicts with keyshave
andversion
and values as names for defines. By default it’s True. Each package can have ‘HAVE_PKGNAME’ and ‘PKGNAME_VERSION’ define where PKGNAME is a package name in upper case. And it’s default patterns. But you can set custom defines. Name of ‘PKGNAME_VERSION’ is used only ifpkg-version
is True.The
pkg-version
parameter can be used to get ‘define’ with version of a package. It can be True of False. If it’s True then ‘define’ will be set. If it’s False then corresponding ‘define’ will not be set. It’s False by default. This parameter will not set ‘define’ ifdefnames
is False.The
def-pkg-vars
parameter can be used to set custom values of variables for ‘pkg-config’. It must bedict
where keys and values are names and values of these variables. ZenMake uses the command line option--define-variable
for this parameter. It’s empty by default.The
tool-atleast-version
parameter can be used to check minimum version of selected tool (pkg-config).Examples in YAML format:
# Elements like 'tasks' and other task params are skipped # ZenMake will check package 'gtk+-3.0' and set define 'HAVE_GTK_3_0=1' configure: - do: pkgconfig packages: gtk+-3.0 # ZenMake will check packages 'gtk+-3.0' and 'pango' and # will check 'gtk+-3.0' version > 1 and <= 100. # Before checking of packages ZenMake will check that 'pkg-config' version # is greater than 0.1. # Also it will set defines 'WE_HAVE_GTK3=1', 'HAVE_PANGO=1', # GTK3_VER="gtk3-ver" and LIBPANGO_VER="pango-ver" where 'gtk3-ver' # and 'pango-ver' are values of current versions of # 'gtk+-3.0' and 'pango'. configure: - do: pkgconfig packages: 'gtk+-3.0 > 1 pango gtk+-3.0 <= 100' tool-atleast-version: '0.1' pkg-version: true defnames: gtk+-3.0: { have: WE_HAVE_GTK3, version: GTK3_VER } pango: { version: LIBPANGO_VER }Examples in Python format:
# Elements like 'tasks' and other task params are skipped # ZenMake will check package 'gtk+-3.0' and set define 'HAVE_GTK_3_0=1' 'configure' : [ { 'do' : 'pkgconfig', 'packages' : 'gtk+-3.0' }, ] # ZenMake will check packages 'gtk+-3.0' and 'pango' and # will check 'gtk+-3.0' version > 1 and <= 100. # Before checking of packages ZenMake will check that 'pkg-config' version # is greater than 0.1. # Also it will set defines 'WE_HAVE_GTK3=1', 'HAVE_PANGO=1', # GTK3_VER="gtk3-ver" and LIBPANGO_VER="pango-ver" where 'gtk3-ver' # and 'pango-ver' are values of current versions of # 'gtk+-3.0' and 'pango'. 'configure' : [ { 'do' : 'pkgconfig', 'packages' : 'gtk+-3.0 > 1 pango gtk+-3.0 <= 100 ', 'tool-atleast-version' : '0.1', 'pkg-version' : True, 'defnames' : { 'gtk+-3.0' : { 'have' : 'WE_HAVE_GTK3', 'version': 'GTK3_VER' }, 'pango' : { 'version': 'LIBPANGO_VER' }, }, }, ],do
=toolconfig
Parameters:
toolname
= ‘pkg-config’,toolpaths
,args
= ‘–cflags –libs’,static
= False,parse-as
= ‘flags-libs’,defname
,var
,msg
,mandatory
= True.Supported languages: any.
Execute any
*-config
tool. It can be pkg-config, sdl-config, sdl2-config, mpicc, etc.ZenMake doesn’t know which tool will be used and therefore this action can be used in any task including standalone runcmd task.
The
toolname
parameter must be used to set name of such a tool. Thetoolpaths
parameter can be used to set paths to find the tool, but usually you don’t need to use it.The
args
parameter can be used to set command line arguments. By default it is ‘–cflags –libs’.The
static
parameter means forcing of static libraries and it is ignored ifparse-as
is not set to ‘flags-libs’.The
parse-as
parameter describes how to parse output. If it is ‘none’ then output will not be parsed. If it is ‘flags-libs’ then ZenMake will try to parse the output for compiler/linker options but ZenMake knows how to parse C/C++ compiler/linker options only, other languages are not supported for this value. And if it is ‘entire’ then output will not be parsed but value of output will be set to define name from thedefname
and/orvar
if they are defined. By defaultparse-as
is set to ‘flags-libs’.The
defname
parameter can be used to set C/C++ define. Ifparse-as
is set to ‘flags-libs’ then ZenMake will try to set define name by using value of thetoolname
discarding ‘-config’ part if it exists. For example if thetoolname
is ‘sdl2-config’ then ‘HAVE_SDL2=1’ will be used. For other values ofparse-as
there is no default value fordefname
but you can set some custom define name.The
var
parameter can be used to set substitution variable name. This parameter is ignored if value ofparse-as
is not ‘entire’. By default it is not defined.The
msg
parameter can be used to set custom message for this action.Examples in YAML format:
tasks: myapp: # other task params are skipped configure: # ZenMake will get compiler/linker options for SDL2 and # set define to 'HAVE_SDL2=1' - do: toolconfig toolname: sdl2-config # ZenMake will get SDL2 version and put it in the define 'SDL2_VERSION' - do: toolconfig toolname: sdl2-config msg: Getting SDL2 version args: --version parse-as: entire defname: SDL2_VERSIONExamples in Python format:
tasks = { 'myapp' : { # other task params are skipped 'configure' : [ # ZenMake will get compiler/linker options for SDL2 and # set define to 'HAVE_SDL2=1' { 'do' : 'toolconfig', 'toolname' : 'sdl2-config' }, # ZenMake will get SDL2 version and put it in the define 'SDL2_VERSION' { 'do' : 'toolconfig', 'toolname' : 'sdl2-config', 'msg' : 'Getting SDL2 version', 'args' : '--version', 'parse-as' : 'entire', 'defname' : 'SDL2_VERSION', }, ] }, }do
=write-config-header
Parameters:
file
= ‘’,guard
= ‘’,remove-defines
= True,mandatory
= True.Supported languages: C, C++.
Write a configuration header in the build directory after some configuration actions. By default file name is
<task name>_config.h
. Theguard
parameter can be used to change C/C++ header guard. Theremove-defines
parameter means removing the defines after they are added into configuration header file and it is True by default.In your C/C++ code you can just include this file like that:
#include "yourconfig.h"You can override file name by using the
file
parameter.do
=parallel
Parameters:
actions
,tryall
= False,mandatory
= True.Supported languages: all languages supported by ZenMake.
Run configuration actions from the
actions
parameter in parallel. Not all types of actions are supported. Allowed actions arecheck-headers
,check-libs
,check-code
andcall-pyfunc
.If you use
call-pyfunc
inactions
you should understand that python function must be thread safe. If you don’t use any shared data in such a function you don’t need to worry about concurrency.If the
tryall
parameter is True then all configuration actions from theactions
parameter will be executed despite of errors. By default thetryall
is False.You can control order of the actions by using the parameters
before
andafter
with the parameterid
. For example, one action can haveid = 'base'
and then another action can haveafter = 'base'
.
Any configuration action has the mandatory
parameter which is True by default.
It also has effect for any action inside actions
for parallel actions and for the whole bundle of parallel actions as well.
All results (defines and some other values) of configuration actions
(excluding call-pyfunc
) in one build
task can be exported to all dependent build tasks.
Use export with the name config-results
for this ability. It allows you to avoid writing the same config actions in tasks
and reduce configuration actions time run.
Example in python format:
def check(**kwargs):
buildtype = kwargs['buildtype']
# some checking
return True
tasks = {
'myapp' : {
'features' : 'cxxshlib',
'libs' : ['m', 'rt'],
# ...
'configure' : [
# do checking in function 'check'
check,
# Check libs from param 'libs'
# { 'do' : 'check-libs' },
{ 'do' : 'check-headers', 'names' : 'cstdio', 'mandatory' : True },
{ 'do' : 'check-headers', 'names' : 'cstddef stdint.h', 'mandatory' : False },
# Each lib will have define 'HAVE_LIB_<LIBNAME>' if autodefine = True
{ 'do' : 'check-libs', 'names' : 'pthread', 'autodefine' : True,
'mandatory' : False },
{ 'do' : 'find-program', 'names' = 'python' },
{ 'do' : 'parallel',
'actions' : [
{ 'do' : 'check-libs', 'id' : 'syslibs' },
{ 'do' : 'check-headers', 'names' : 'stdlib.h iostream' },
{ 'do' : 'check-headers', 'names' : 'stdlibasd.h', 'mandatory' : False },
{ 'do' : 'check-headers', 'names' : 'string', 'after' : 'syslibs' },
],
'mandatory' : False,
#'tryall' : True,
},
#{ 'do' : 'write-config-header', 'file' : 'myapp_config.h' }
{ 'do' : 'write-config-header' },
],
},
}
Dependencies¶
ZenMake supports several types of dependencies for build projects:
System libraries¶
System libraries can be specified by using the config parameter libs. Usually you don’t need to set paths to system libraries but you can set them using the config parameter libpath.
Local libraries¶
Local libraries are libraries from your project. Use the config parameter use to specify such dependencies.
Sub buildconfs¶
You can organize building of your project by using more than one buildconf file in some sub directories of your project. In this case ZenMake merges parameters from all such buildconf files. But you must specify these sub directories by using the config parameter subdirs.
Parameters in the sub buildconf can always overwrite matching parameters from the parent buildconf. But some parameters are not changed.
These parameters can be set only in the the top-level buildconf:
buildroot
,realbuildroot
,project
,general
,cliopts
Also default build type can be set only in the top-level buildconf.
These parameters are always used without merging with parent buildconfs:
startdir
,subdirs
,tasks
ZenMake doesn’t merge your own variables in your buildconf
files if you use some of them.
Other variables are merged including byfilter
. But build tasks in
the byfilter
which are not from the current buildconf are ignored
excepting explicit specified ones.
Some examples can be found in the directory ‘subdirs’ in the repository here.
External dependencies¶
A few basic types of external dependencies can be used:
See full description of buildconf parameters for external dependencies here.
ZenMake projects¶
Configuration for this type of dependency is simple in most cases: you set up the config variable edeps with the rootdir and the export-includes (if it’s necessary) and then specify this dependency in use, using existing task names from dependency buildconf.
Example in YAML format:
edeps:
zmdep:
rootdir: ../zmdep
export-includes: ../zmdep
tasks:
myutil:
features : cxxshlib
source : 'shlib/**/*.cpp'
# Names 'calclib' and 'printlib' are existing tasks in 'zmdep' project
use: zmdep:calclib zmdep:printlib
Example in Python format:
edeps = {
'zmdep' : {
'rootdir': '../zmdep',
'export-includes' : '../zmdep',
},
}
tasks = {
'myutil' : {
'features' : 'cxxshlib',
'source' : 'shlib/**/*.cpp',
# Names 'calclib' and 'printlib' are existing tasks in 'zmdep' project
'use' : 'zmdep:calclib zmdep:printlib',
},
}
Additionally, in some cases, the parameter buildtypes-map can be useful.
Also it’s recommended to use always the same version of ZenMake for all such projects. Otherwise there are some compatible problems can be occured.
Note
Command line options --force-edeps
and --buildtype
for current project will affect rules for its external dependencies
while all other command line options will be ignored.
You can use environment variables
to have effect on all external dependencies. And, of course, you can set up
each buildconf in the dependencies to have desirable behavior.
Non-ZenMake projects¶
You can use external dependencies from some other build systems but in this
case you need to set up more parameters in the config
variable edeps. Full description of these
parameters can be found here. Only one parameter
buildtypes-map
is not used for such dependencies.
If it’s necessary to set up different targets for different buildtypes you can use selectable parameters in build tasks of your ZenMake project.
Example in Python format:
foolibdir = '../foo-lib'
edeps = {
'foo-lib-d' : {
'rootdir': foolibdir,
'export-includes' : foolibdir,
'targets': {
'shared-lib' : {
'dir' : foolibdir + '/_build_/debug',
'type': 'shlib',
'name': 'fooutil',
},
},
'rules' : {
'build' : 'make debug',
},
},
'foo-lib-r' : {
'rootdir': foolibdir,
'export-includes' : foolibdir,
'targets': {
'shared-lib' : {
'dir' : foolibdir + '/_build_/release',
'type': 'shlib',
'name': 'fooutil',
},
},
'rules' : {
'build' : 'make release',
},
},
}
tasks = {
'util' : {
'features' : 'cxxshlib',
'source' : 'shlib/**/*.cpp',
},
'program' : {
'features' : 'cxxprogram',
'source' : 'prog/**/*.cpp',
'use.select' : {
'debug' : 'util foo-lib-d:shared-lib',
'release' : 'util foo-lib-r:shared-lib',
},
},
}
Common notes¶
You can use command line option -E
/--force-edeps
to run rules for
external dependencies without checking triggers.
Some examples can be found in the directory ‘external-deps’ in the repository here.
Tests¶
ZenMake supports building and running tests. It has no special support for particular testing framework/library but it can be any testing framework/library.
To set up a test you need to specify task feature test
in buildconf
file.
Then you have a choice:
- If selected task has feature *program then you may not need to do anything more. ZenMake wil try to build/run this task as test as is. But you can specify task parameter run to set up additional arguments.
- If selected task has no feature *program and has no run but has *stlib/*shlib then this task is considered as a task with test but ZenMake will not try to run this task as a test. It’s useful for creation of separated libraries for tests only.
- Specify task parameter run.
Tests are always built only on build
stage and run only on test
stage.
Order of buiding and running test tasks is controlled by their depedencies
as for just build tasks. So it’s possible to use task parameter use
to
control order of running of tests.
Example of test tasks in YAML format:
stlib-test:
features : cxxprogram test'
source : tests/test_stlib.cpp
# testcmn here is some library with common code for tests
use : stlib testcmn
test from script:
features: test
run:
cmd : python tests/test.py
cwd : .
shell: false
use: complex
configure: [ { do: find-program, names: python } ]
# testcmn is a library with common code for tests only
testcmn:
features: cxxshlib test
source : tests/common.cpp
includes: .
shlib-test:
features: cxxprogram test
source : tests/test_shlib.cpp
use : shlib testcmn
run:
cmd : '$(tgt) a b c'
env : { AZ : '111', BROKEN_TEST : 'false' }
repeat : 2
timeout : 10 # in seconds
shell : false
shlibmain-test:
features: cxxprogram test
source : tests/test_shlibmain.cpp
use : shlibmain testcmn
Example of test tasks in python format:
'stlib-test' : {
'features' : 'cxxprogram test',
'source' : 'tests/test_stlib.cpp',
# testcmn here is some library with common code for tests
'use' : 'stlib testcmn',
},
'test from script' : {
'features' : 'test',
'run' : {
'cmd' : 'python tests/test.py',
'cwd' : '.',
'shell' : False,
},
'use' : 'complex',
'configure' : [ dict(do = 'find-program', names = 'python'), ]
},
# testcmn is a library with common code for tests only
'testcmn' : {
'features' : 'cxxshlib test',
'source' : 'tests/common.cpp',
'includes' : '.',
},
'shlib-test' : {
'features' : 'cxxprogram test',
'source' : 'tests/test_shlib.cpp',
'use' : 'shlib testcmn',
'run' : {
'cmd' : '$(tgt) a b c',
'env' : { 'AZ' : '111', 'BROKEN_TEST' : 'false'},
'repeat' : 2,
'timeout' : 10, # in seconds
'shell' : False,
},
},
'shlibmain-test' : {
'features' : 'cxxprogram test',
'source' : 'tests/test_shlibmain.cpp',
'use' : 'shlibmain testcmn',
},
Use can build and/or run tests with command test
. You can do it with
command build
as well but build
doesn’t do it by default, only if some
command line arguments are used.
To build and run all tests with command test
:
zenmake test
The same action with command build
:
zenmake build -t yes -T all
To build but not run tests with command test
:
zenmake test -T none
You can run all tests but also you can run tests only on changes. For this you can
use --run-tests
with value on-changes
:
zenmake test -T on-changes
To specify additional command line arguments for all compiled testing
executables you can use --
:
zenmake test -- -vs
Everything after --
is considered as extra command line arguments
for executable target files.
Performance tips¶
Here are some tips which can help to improve performance of ZenMake in some cases.
Hash algorithm¶
By default ZenMake uses sha1 algorithm to control changes of config/built files and for some other things. Modern CPUs often have support for this algorithm and sha1 shows better or almost the same performance as md5 in this cases. But in some other cases md5 can be faster and you can switch to use this hash algorithm. However, don’t expect a big difference in performance of ZenMake.
It’s recommended to check if it really has positive effect before using of md5.
To change hash algorithm you can use parameter hash-algo
in buildconf
general features.
FAQ¶
Why is python used?
It’s mostly because Waf is implemented in python.
Can I use buildconf.py as usual python script?
Yes, you can. Such a behavior is supported while you don’t try to use reserved config variable names for inappropriate reasons.
I want to install my project via zenmake without ‘bin’ and ‘lib64’ in one directory
Example on Linux:
DESTDIR=your_install_path PREFIX=/ BINDIR=/ LIBDIR=/ zenmake install
or:
PREFIX=your_install_path BINDIR=your_install_path LIBDIR=your_install_path zenmake install
Changelog¶
Version 0.11.0 (2022-09-04)¶
- Added
- embed pyyaml
- add value ‘all’ for variable ‘for’ in the ‘byfilter’ parameter
- add buildconf parameter export-* for libpath, stlibpath and all *flags
- add the ‘cleanall’ command as replacement for the ‘distclean’ command
- remake/improve/extend substitutions (buildconf variables inside a text)
- add some syntactic sugar for buildconf
- get rid of ${TARGET} and rewrite substitution of ${SRC} and ${TGT}
- add ability to use ‘and’, ‘or’ and ‘not’ in the ‘*.select’
- add ‘host-os’ and ‘distro’ for the ‘*.select’ conditions
- add ‘if’ for the ‘byfilter’ parameter
- add the ‘run’ command
- support qt5 for c++ (almost done) #31
- enable absolute paths in path patterns
- add runtime lib paths for the ‘run’ command and for the ‘run’ feature
- support python 3.10
- Changed
- update waf to 2.0.23
- fix bug with auto detection of interpreter in ‘runcmd’
- rename ‘include’ to ‘incl’ and ‘exclude’ to ‘excl’ for buildconf parameter ‘source’
- rename buildconf parameter ‘matrix’ to ‘byfilter’
- rename ‘export-config-actions’ to ‘export-config-results’
- rename buildconf parameter ‘config-actions’ to ‘configure’
- remake and improve the buildconf parameters ‘export-*’
- prioritize yaml buildconf format
- fix bug of no automatic reconfiguration with changed env/cli args for install/uninstall
- rename buildconf ‘features’ to ‘general’
- fix bug with ‘enabled.select’
- improve buildconf validator
- extend/improve install directory vars
- fix problem when not all values from buildconf.cliopts have effect
- fix order of reading config values from env, cli and config file
- fix terminal width detection in CLI
- improve system libraries detection
- fix bug when zenmake could not find toolchain from sys env vars like CC, CXX, etc
- fix problem with found zero-byte executables (mostly windows problem)
- fix problem with short file names (8.3 filename) on windows
- fix bug when getting rid of CXX in cmd line does not induce reconfigure
- make stop child procces in the ‘run’ command on keyboard interrupt
- many other fixes
- Removed
- drop python 2.x, 3.4 and pypy
- remove task features aliases: more problems than profits
- remove redundant ‘default-buildtype’ parameter
- remove the ‘platforms’ parameter
Version 0.10.0 (2020-09-23)¶
- Added
- support Fortran language
- add basic D language support
- add selectable parameters for buildconf task parameters
- support external dependencies
- add ‘tryall’ and ‘after’/’before’ for parallel configuration actions
- add correct buildconf validation for nested types
- add configuration action ‘call-pyfunc’ (‘check-by-pyfunc’) to parallel actions
- add configuration action ‘check-code’
- add configuration actions ‘pkgconfig’ and ‘toolconfig’ (support pkg-config and other *-config tools)
- add configuration action ‘find-file’
- add ‘remove-defines’ for configuration action ‘write-config-header’
- add option to add extra files to monitor (‘monitor-files’)
- add buildconf task parameters ‘stlibs’ and ‘stlibpath’
- add buildconf task parameters ‘monitlibs’ and ‘monitstlibs’
- add buildconf task parameter ‘export-config-actions’
- add buildconf task parameter ‘enabled’
- add buildconf task parameter ‘group-dependent-tasks’
- add add buildconf task parameter ‘install-files’
- add parameter ‘build-work-dir-name’ to buildconf ‘features’
- add simplified form of patterns using for buildconf task parameter ‘source’
- add custom substitution variables
- add detection of msvc, gfortran, ifort and D compilers for command ‘sysinfo’
- add number of CPUs for command ‘sysinfo’
- add ‘not-for’ condition for config var ‘matrix’
- add ability to set compiler flags in buildconf parameter ‘toolchains’
- add ability to use ‘run’ in buildconf as a string or function
- add cdmline options –verbose-configure (-A) and –verbose-build (-B)
- add cmdline option ‘–force-edeps’
- add c++ demo project with boost libraries
- add demo project with luac
- add demo project with ‘strip’ utility on linux
- add demo project with dbus-binding-tool
- add demo projects for gtk3
- add demo project for sdl2
- add codegen demo project
- Changed
- improve support of spaces in values (paths, etc)
- improve unicode support
- use sha1 by default for hashes
- correct some english text in documentation
- detach build obj files from target files
- remove locks in parallel configuration actions
- small optimization of configuration actions
- improve validation for parallel configuration actions
- improve error handling for configuration actions with python funcs
- improve buildconf errors handling
- improve use of buildconf parameter ‘project.version’
- remake/improve handling of cache/db files (see buildconf parameter ‘db-format’)
- reduce size of zenmake.pyz by ignoring some unused waf modules
- apply solution from waf issue 2272 to fix max path limit on windows with msvc
- rename ‘–build-tests’ to ‘–with-tests’, enable it for ‘configure’ and add ability to use -t and -T as flags
- rename ‘sys-lib-path’ to ‘libpath’ and fix bug with incorrect value
- rename ‘sys-libs’ to ‘libs’
- rename ‘conftests’ to ‘config-actions’
- rename config action ‘check-programs’ to ‘find-program’ and change behaviour
- make ordered configuration actions
- disable ‘:’ in task names
- refactor code to support task features in separated python modules
- don’t merge buildconf parameter ‘project’ in sub buildconfs (see ‘subdirs’)
- fix bug with toolchain supported more than one language
- fix some bugs with env vars
- fix compiling problem with the same files in different tasks
- fix bug with object file indexes
- fix command ‘clean’ for case when build dir is symlink
- fix Waf bug of broken ‘vnum’ for some toolchains
- fix parsing of cmd line in ‘runcmd’ on windows
- fix processing of destdir, prefix, bindir, libdir
- Removed
- remove configuration action (test) ‘check’
Version 0.9.0 (2019-12-10)¶
- Added
- add config parameter ‘startdir’
- add config parameter ‘subdirs’ to support sub configs
- add ‘buildroot’ as the command-line arg and the environment variable
- print header with some project info
- add parallel configuration tests
- Changed
- fix default command-line command
- fix problem of too long paths in configuration tests on Windows
- fix some small bugs in configuration tests
- rid of the wscript file during building
- improve buildconf validator
- improve checking of the task features
- update Waf to version 2.0.19
- Removed
- remove config parameters ‘project.root’ and ‘srcroot’
License¶
BSD 3-Clause License
Copyright (c) 2019, 2020 Alexander Magola All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.