BuildExe as a Package Manager#
When we are compiling our “script” to an executable we can also add additional library build files which define how the library is built.
The procedure is similar to git cloning the library to the ENV{BUILDCC_HOME}/libs folder using the libs
options in buildexe.
Please see BuildExe Setup to setup your libs folder appropriately.
Basic Procedure#
Helloworld “fmtlib” example#
Git clone the
fmt
library into yourENV{BUILDCC_HOME}/libs
folder- Run
buildexe libs --help-all
. You should automatically see the library folder name pop up under the
libs
submodule.In this case it will be the
fmt
option.
- Run
1script
2 Options:
3 --configs TEXT ... Config files for script mode
4
5libs
6 Libraries
7 Options:
8 --fmt TEXT ... fmt library
Since we want to use the
fmt
library in our project we can now write ourcompile.toml
file as given below. (See highlighted lines)We then write our “script”, include the
fmt
build header file and define our targets and dependencies.Lastly we invoke buildexe to build our project
buildexe --config compile.toml --config $BUILDCC_HOME/host/host_toolchain.toml
Directory structure#
Write your fmtlib build files#
Note
This process might seem like a hassle. But please note that fmtlib does not currently have support for BuildCC like build files and it must be provided by the user.
1#pragma once
2
3#include "buildcc.h"
4
5using namespace buildcc;
6
7/**
8* @brief User configurable options
9* default_flags: Adds default preprocessor, compile and link flags to the fmt
10* library if true. If false these would need to be provided by the user.
11*/
12struct FmtConfig {
13 bool default_flags{true};
14 // NOTE, Add more options here as required to customize your fmtlib build
15};
16
17/**
18* @brief Build the libfmt static or dynamic library
19*
20* @param target Initialized specialized library target
21* @param config See FmtConfig above
22*/
23void build_fmt_cb(BaseTarget& target, const FmtConfig& config = FmtConfig());
24
25/**
26* @brief Information for fmt header only library
27*
28* @param target_info Holds the include dirs, headers and preprocessor flag
29* information
30*/
31void build_fmt_ho_cb(TargetInfo& target_info);
1#include "build.fmt.h"
2
3void build_fmt_cb(BaseTarget& target, const FmtConfig& config) {
4 target.AddSource("src/os.cc");
5 target.AddSource("src/format.cc");
6 target.AddIncludeDir("include", false);
7 target.GlobHeaders("include/fmt");
8
9 // Toolchain specific flags added
10 // if default_flags == true
11 if (config.default_flags) {
12 switch (target.GetToolchain().GetId()) {
13 case ToolchainId::Gcc:
14 target.AddCppCompileFlag("-std=c++11");
15 break;
16 case ToolchainId::MinGW:
17 target.AddCppCompileFlag("-std=c++11");
18 break;
19 case ToolchainId::Msvc:
20 target.AddCppCompileFlag("/std:c++11");
21 break;
22 default:
23 break;
24 }
25 }
26
27 // Register your fmt lib tasks
28 target.Build();
29}
30
31void build_fmt_ho_cb(TargetInfo& target_info) {
32 target_info.AddIncludeDir("include", false);
33 target_info.GlobHeaders("include/fmt");
34 target_info.AddPreprocessorFlag("-DFMT_HEADER_ONLY=1");
35}
Write your C++ “script”#
Boilerplate is similar to the BuildExe helloworld “script” example in BuildExe “Script” example
Core build setup is highlighted below
On line 4 we include our
build.fmt.h
include file. Seecompile.toml
libs submodule to correlate- On line 8 we include the
buildexe_lib_dirs.h
include file. This is a generated include file which contains the absolute paths of the library folders. Access is through
BuildExeLibDir::[lib_folder_name]
This is the reason why we need to make sure that our git cloned library folder name is also a valid C++ variable name.
- On line 8 we include the
- On line 40 we point to the absolute
fmt
libs folder path for the sources and redirect the output to ourenv::get_project_build_dir() / "fmt"
folder. In this way we can safely use out of root projects and redirect the output files to our build location
There are other input source -> output object redirect options through additional APIs.
- On line 40 we point to the absolute
On line 43 and 44 we directly use our fmtlib build APIs to define how fmtlib should be built
- On line 47 and 48 we define our Hello World executable target
See
main.cpp
below for fmtlib hello world exampleSee
hello_world_build_cb
for build steps
- On line 79
hello_world_build_cb
in additional to compiling ourmain.cpp
file We need to link our compiled
fmt_lib
using theAddLibDep
APIWe also insert the
fmt_lib
include dirs to the hello world target since we need to#include "fmt/format.h"
inmain.cpp
- On line 79
- On line 52 we register a dependency of
fmt_lib
onhello_world
. This guarantees that the fmt library will be built before the hello world executable.
This is essential because we need to link fmtlib with our hello world executable.
- On line 52 we register a dependency of
1#include "buildcc.h"
2
3// Included through libs
4#include "build.fmt.h"
5
6// Generated by BuildCC
7// See the `_build_internal` directory
8#include "buildexe_lib_dirs.h"
9
10using namespace buildcc;
11
12// Function Prototypes
13static void clean_cb();
14static void hello_world_build_cb(BaseTarget &target, BaseTarget &fmt_lib);
15
16int main(int argc, char **argv) {
17 // Get arguments
18 ArgToolchain arg_gcc;
19
20 Args::Init()
21 .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc)
22 .Parse(argc, argv);
23
24 // Initialize your environment
25 Reg::Init();
26
27 // Pre-build steps
28 Reg::Call(Args::Clean().Func(clean_cb);
29
30 // Build steps
31 // Explicit toolchain - target pairs
32 Toolchain_gcc gcc;
33
34 // Setup your [Library]Target_[toolchain] fmtlib instance
35 // Update your TargetEnv to point to `BuildExeLibDir::fmt` folder
36 // The generated build files will go into your current `project_build_dir / fmt` folder
37 StaticTarget_gcc fmt_lib(
38 "libfmt", gcc,
39 TargetEnv(BuildExeLibDir::fmt, env::get_project_build_dir() / "fmt"));
40
41 // We use the build.fmt.h and build.fmt.cpp APIs to define how we build our fmtlib
42 FmtConfig fmt_config;
43
44 // Define our hello world executable
45 ExecutableTarget_gcc hello_world("hello_world", gcc, "");
46
47 // Fmt lib is a dependency to the Hello world executable
48 // This means that fmt lib is guaranteed to be built before the hello world executable
49 Reg::Toolchain(arg_gcc.state)
50 .Build(arg_gcc.state, build_fmt_cb, fmt_lib, fmt_config)
51 .Build(hello_world_build_cb, hello_world, fmt_lib)
52 .Dep(hello_world, fmt_lib)
53 .Test("{executable}", hello_world);;
54
55
56 // Build and Test Target
57 // Builds libfmt.a and ./hello_world
58 Reg::Run();
59
60 // Post Build steps
61 // - Clang Compile Commands
62 plugin::ClangCompileCommands({&hello_world}).Generate();
63 // - Graphviz dump
64 std::cout << reg.GetTaskflow().dump() << std::endl;
65
66 return 0;
67}
68
69static void clean_cb() {
70 fs::remove_all(env::get_project_build_dir());
71}
72
73static void hello_world_build_cb(BaseTarget &target, BaseTarget &fmt_lib) {
74 target.AddSource("main.cpp", "src");
75
76 // Add fmt_lib as a library dependency
77 target.AddLibDep(fmt_lib);
78 // We need to insert the fmt lib include dirs and header files into our hello_world executable target (naturally)
79 target.Insert(fmt_lib, {
80 SyncOption::IncludeDirs,
81 SyncOption::HeaderFiles,
82 });
83
84 // Register your tasks
85 target.Build();
86}
Write your compile.toml
file#
The only difference from the
compile.toml
in BuildExe “Script” example is the additional of thelibs
submoduleWe use the
fmt
option since we git cloned the library into the libs folderWe add the various fmt build files that need to be compiled with our “script”
See highlighed lines 19 and 20
1# Settings
2root_dir = ""
3build_dir = "_build_internal"
4loglevel = "debug"
5clean = false
6
7# BuildExe run mode
8mode = "script"
9
10# Target information
11name = "single"
12type = "executable"
13relative_to_root = ""
14srcs = ["build.main.cpp"]
15
16[script]
17configs = ["build.toml"]
18
19[libs]
20fmt = ["build.fmt.cpp", "build.fmt.h"]
Write your build.toml
file#
Exact same
build.toml
as seen in the document BuildExe “Script” example
1# Root
2root_dir = ""
3build_dir = "_build"
4loglevel = "debug"
5clean = true
6
7# Toolchain
8[toolchain.gcc]
9build = true
10test = true
Write your main.cpp
helloworld example in fmtlib#
#include "fmt/format.h"
int main() {
fmt::print("{} {}", "Hello", "World");
return 0;
}