Skip to content

podusowski/pake

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Welcome to another C++ build tool!

(example of pake script which does some more things like db generation, see more at rusted)

What is the difference?

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 use python or bash 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.

Meeh, another CMake?

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 Makefiles, only that CMake syntax is far from being a programming language.

I could stick with Makefiles then

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.

They say Ninja is modern, cool and fast

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.

How to get it?

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.

Tutorial

Starting a 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.

Adding more files to the project

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)

Variable defining and dereferencing order

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

Defining compiler flags to the target

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.

What's wrong with compiler_flags(-Iincludes)?

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}").

Bulding static libraries

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.

Splitting your build script into modules

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.

Passing variables between the modules

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.

Passing include directories between modules

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")

Special modules and variables

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.

Using configurations

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.

More documentation

Stay tuned for more docs here... in the mean time, see the wiki pages, there is some possibly outdated info there.

About

Friendly C++ build system which doesn't follow trends

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published