(example of pake script which does some more things like db generation, see more at rusted)
In brief:
pake
's script does not allow any logic, it means, you can not make conditions, loops and so on, if you want it, just usepython
orbash
as a prerequisite (pake
suports running those before and after building something).- It's not a "build system generator", you run it, you get things compiled.
No, CMake
is a build system generator which provides some sort of programming language to do so. It's like writing a python
script which generates Makefile
s, only that CMake
syntax is far from being a programming language.
You can, but it's too low-level, make
doesn't understand C++ project structure by itself, it doesn't know that your .cpp file include some .hpp, so if this .hpp changes, then .cpp should be rebuilt. You have to write such dependencies (nobody does that) or dependency generation support yourself.
It is, but its scripts are not supposed to be written by hand, see their description: "it is designed to have its input files generated by a higher-level build system". But CMake
has a generator for it, so if for some reason you won't like pake, I would recommend CMake+Ninja
.
Because pake is a alpha quality software, the only official way of distributing it is to "build" it using waffle and put in inside your project repository. To simplify this, there is a shell script called make_pake.sh
which generates __build/pake.py
file which you can directly put and use in your project.
After putting pake.py somewhere suitable (for example in your project directory), create a pake module called build.pake
. Note that only .pake
extention is important, pake looks for all .pake
files (we call them modules) inside your projects directory and you don't need to define them anywhere. Inside your build.pake file, put something like this:
target application my_app sources(main.cpp) # sup, I'm a comment!
this will define a target my_app
which is statically linked application built from main.cpp
. To build it, just type ./pake.py my_app
or ./pake.py -a
if you want to build every target found in your tree. After building, your application will be placed into __build/__default
directory. In case you wonder, __default
is directory named from current build configuration, you will hear about them later.
Say, you want to add some new file into your project, you can write:
target application my_app sources(main.cpp additional_file.cpp)
this will compile main.cpp
and additional_file.cpp
and link them together. When your list gets too big, you can split it by escaping new line:
target application my_app \
sources(main.cpp \
additional_file.cpp)
or you can use variables:
append $sources main.cpp
append $sources additional_file.cpp
target application my_app sources($sources)
What is neat in pake variable system, is that you can define and reference the variables in any order you like, for example, you can do something like this:
target application my_app sources($all_sources yet_another_file.cpp)
append $sources $main_file
append $sources additional_file.cpp
append $main_file main.cpp
Very often, there is a need for adding compiler switches to the target, there is a possibility to add them to the whole tree, but let us keep it simple for now. To add those, you just need to add compiler_flags
parameter to target definition. You can add the directories in which compiler will look for header files, but this way is not very scalable, instead, use include_dirs
for that:
append $sources main.cpp
append $sources additional_file.cpp
target application my_app \
sources($sources) \
compiler_flags(-std=c++14) \
include_dirs(includes other_includes)
Hopefully, this example is self-explainatory.
Because you have to add -I
everywhere. For example, every module has special $__path variable, which you can use as your include directory, you can do this like application my_tests include_dirs($gtest.__path)
or application my_tests compiler_flags("-I${gtest.__path}")
.
Static library is just another type of target which pretty much behaves like the application
target, only it builds an .a
file which you can later link your application.
target static_library my_lib sources(library.cpp)
target application my_app link_with(my_lib) depends_on(my_lib) sources(main.cpp)
Few things here, first, the new, link_with
attribute, which doesn't really need to be explained, but why we need depends_on
? It's because you can put inside link_with
everything which understands your linker, include system wide libraries and pake
has no way of knowing if given library should be builts or just passed quietly to the linker.
Until now, all examples assumed that you have one file in your source tree (build.pake
, but if you remember, base name isn't exactly important; actually it is, but we will get there). Pake gives you ability to split such scripts into modules. For example, you can create two files:
# lib.pake
append $sources library.cpp
target static_library my_lib sources($sources)
# app.pake
append $sources main.cpp
target application my_app link_with(my_lib) depends_on(my_lib) sources($sources)
As you can see, module names don't have to be named after targets inside (you couldn't create multiple targets in one module if this would be a requirement). You can move this modules along with their sources everywhere you want, as long as you stay in your source tree, pake
will find them.
Each module has its own variable namespace which isn't implicitly shared between them, in other words, you can create variables with the same name across modules (like $sources
variable in previous example). This doesn't mean that you can't refer to variable from other module:
# lib.pake
# don't create any target, just define variable with the sources
append $sources library.cpp
# app.pake
append $sources main.cpp
append $sources $lib.sources
target application my_app link_with(my_lib) depends_on(my_lib) sources($sources)
It doesn't matter where your modules are, pake
will find them and evaluate variables in proper way.
It's a common case when you have a module with library target and you want to use it in your application target. In this case, it's a good practice to use module's $__path
special variable to point compiler to proper include directories, see the example how can you configure your project to use Google Test
:
# gtest.pake
target static_library gtest include_dirs(.) sources(gmock-gtest-all.cc gmock_main.cc)
Of course you can put gtest directory everywhere you like.
# my_app.pake
target static_library my_app_lib \
sources(my_class.cpp)
target application my_app \
sources(main.cpp) \
link_with(my_app_lib) \
depends_on(my_app_lib)
target application my_app_tests \
sources(my_class_tests.cpp) \
include_dirs($gtest.__path) \
link_with(my_app_lib gtest) \
depends_on(my_app_lib gtest) \
run_after("${__build}/my_app_tests")
There are some so called "special" things predefined in pake
.
`$__build` | Contains patch to the directory where all artefacts lands. |
`$__path` | Contains base patch for the directory where the module in which it is define lays. Use it like `$some_module.__path` |
`$__null` | Just null variable, nothing special, but it's useful when you want to export something from configuration. |
`$__configuration` | It's a special module in which all exported configuration variables are available from. See more at configurations section of this tutorial. |
During pake
invokation, you can define configuration
which is pretty much set of rules which build process will follow. By default, pake
uses predefined configuration called __default
(it is named because you can override it as well as define new one). Now, let us change compiler and flags in default configuration:
configuration __default compiler(clang++) compiler_flags(-std=c++14)
By default pake
, like most build systems out there, uses c++
as a C++ compiler command. In distros like Debian, you can switch it using sudo update-alternatives --config c++
.
You can also create new configurations, see the example how it works:
append $win_graphic_libraries opengl32 # ...
append $linux_graphic_libraries GL # ...
configuration win32 \
compiler(i686-w64-mingw32-c++) \
archiver(i686-w64-mingw32-ar) \
compiler_flags(-m32) \
application_suffix(.exe) \
export($win_graphic_libraries:$graphic_libraries)
configuration linux \
export($linux_graphic_libraries:$graphic_libraries)
target application my_awesome_game \
sources(main.cpp) \
link_with($__configuration.graphic_libraries)
Like the other things, it doesn't matter where you define these configurations, you can put them into some configurations.pake
module or the same module as your targets.
Stay tuned for more docs here... in the mean time, see the wiki pages, there is some possibly outdated info there.