Let’s make a simple Hello World C++ project, and see how HyperMake can help.
Here’s the project directory layout:
ProjectRoot
|
+--inc/
+--src/
+--hello.cpp
In hello.cpp:
#include <iostream>
int main(int argc, char *argv[]) {
std::cout << "Hello World!" << std::endl;
return 0;
}
To build it, use g++ -o hello src/hello.cpp
.
Probably you will need to run sudo apt install g++
first on Debian/Ubuntu if
you’ve never installed the toolchain.
Fortunately, using HyperMake
, you don’t need to worry about that on your host.
HyperMake
is a YAML file which sits in the root directory of source tree.
---
format: hypermake.v0
name: hello
description: The Hello World Project
The first line ---
indicates the start of a new document in YAML
format
is required and must be hypermake.v0
.
hmake only accepts YAML files with format: hypermake.v0
.name
specifies the project name, which is required.description
gives more information about the project. It’s optional.Like make, the minimum execution unit is target:
---
format: hypermake.v0
name: hello
description: The Hello World Project
targets:
build:
description: build hello binary
image: 'gcc:4.9'
cmds:
- g++ -o hello src/hello.cpp
The target build is defined. It has three properties:
description
: a brief intro about what the target does;image
: the docker image to create the container and run commands;cmds
: a list of commands to execute inside the container.Now, let’s do
hmake build
./hello
It shows Hello World
.
It always builds even if you don’t have g++ installed on the host.
If typing hmake build
is boring, defining default targets can simplify the
command as a single hmake
.
---
format: hypermake.v0
name: hello
description: The Hello World Project
targets:
build:
description: build hello binary
image: 'gcc:4.9'
cmds:
- g++ -o hello src/hello.cpp
settings:
default-targets:
- build
Now, typing hmake
is equivalent to hmake build
.
More settings can be added.
For example all the targets use the same image, it can be moved to settings
.
---
format: hypermake.v0
name: hello
description: The Hello World Project
targets:
build:
description: build hello binary
cmds:
- g++ -o hello src/hello.cpp
settings:
default-targets:
- build
docker:
image: 'gcc:4.9'
All targets will have image: 'gcc:4.9'
by default, unless explicitly specified
in target.
Let’s do some tricks here: touch src/hello.cpp
or modify the file, and then
type hmake
. You will see the build
target is skipped:
HyperMake v1.1.0 https://github.com/evo-cloud/hmake
=> build 21:56:42.277
:] build
╔══════╤═══════╤════════╤════════════╤════════════╤═════╗
║Target│Result │Duration│Start │Finish │Error║
╠══════╪═══════╪════════╪════════════╪════════════╪═════╣
║build │Skipped│ │21:56:42.277│21:56:42.277│ ║
╚══════╧═══════╧════════╧════════════╧════════════╧═════╝
OK
This is definitely not what we want.
However hmake doesn’t have the knowledge about source files and output files.
It must be explicitly specified using watches
and/or artifacts
:
targets:
build:
description: build hello binary
watches:
- inc
- src
cmds:
- g++ -o hello src/hello.cpp
artifacts:
- hello
The items in watches
can be a path to a directory or a file, or with wildcards
matching a list of files/directories.
If the item is a directory, all sub-directories and files are watched recursively.
artifacts
lists the output files.
Wildcard is not allowed here, and directory is not matched recursively.
hmake rebuilds the target if any of the watched file is changed or
any artifact is missing.
As the project is so simple that we can use an existing docker image gcc:4.9
which
contains toolchain we need.
However in most cases, the public docker images are not always good enough, and
we want to install extra bits to build the project.
Then we need to build our own toolchain image.
Let’s use cmake
to build our project,
by adding CMakeList.txt
under project root:
cmake_minimum_required(VERSION 2.8.0)
project(hello CXX)
include_directories("inc")
add_executable(hello src/hello.cpp)
Then we will need cmake
in toolchain image, let’s build one based on gcc:4.9
.
Create a folder toolchain
under project root and put a Dockerfile
inside it:
ProjectRoot
|
+--inc/
+--src/
| +--hello.cpp
+--toolchain/
| +--Dockerfile
+--HyperMake
+--CMakeList.txt
In Dockerfile
:
FROM gcc:4.9
RUN apt-get update && apt-get install -y cmake && apt-get clean
And update HyperMake
:
---
format: hypermake.v0
name: hello
description: The Hello World Project
targets:
toolchain:
description: build our own toolchain image
watches:
- toolchain
build: toolchain
build:
description: build hello binary
after:
- toolchain
watches:
- inc
- src
cmds:
- rm -fr rel && mkdir -p rel
- cd rel && cmake .. && make
artifacts:
- rel/hello
settings:
default-targets:
- build
docker:
image: 'cmake-gcc:4.9'
Target toolchain
is added. Property build
tells hmake to build a
docker image from toolchain/Dockerfile
.
And as image: cmake-gcc:4.9
is specified in settings
,
the built image will be cmake-gcc:4.9
.
In target build
, property after
specifies toolchain
must succeed
before build
is able to run.
Now, type hmake
and it will first run toolchain
to build cmake-gcc:4.9
and
the run build
to call cmake
to build the binary.