Andrew King

Software developer - C++, Web & Games

Mastodon: @rewrking@mastodon.gamedev.place

Bluesky: @rewrking

Github: @rewrking

Linkedin: andrewraymondking

How to build a new build system...

Existing markup-based C++ project formats

Build system File extension File format Platforms Notes
MSBuild .vcxproj XML Windows for Visual Studio
xcodebuild .pbxproj Next-style Property List macOS for Xcode
CodeLite .project XML All for CodeLite
Code::Blocks .cbp XML All for Code::Blocks

Existing script-based build file formats

Build system File name / ext File format Platforms Notes
CMake CMakeLists.txt
.cmake
Proprietary All Build system generator
Requires project-specific properties *
Numerous gotchas
Ninja .ninja Proprietary All Designed to be generated
GNU Make Makefile
.make
Proprietary All * Windows requires mingw32-make,
NMAKE or Qt Jom
Bazel .bzl Proprietary / Starlark All Specializes in distributed builds
Meson meson.build Proprietary All Build system generator
qmake .pro Proprietary All for QtCreator *
Premake premake5.lua Lua All Build system generator
xmake xmake.lua Lua All Has a package manager solution

Where can build systems improve?

  • Accessibility: beginners shouldn't have to learn a language to write a language
  • Markup is easy to parse and maintain, yet most build systems use scripting
  • Markup can also be matched against a schema, providing better tooling
  • Package managers are seen as a separate tool, yet they have to build code from source

What can we automate?

  • Project initialization: requires separate tools, a template project, or IDE involvement
  • Fetching depedencies
  • Package management & using separate package managers
  • Archiving / creating distributions
  • Typical things we do inside of shell scripts, outside of builds, or between steps
  • Anything that gets us to a "warm & fuzzy" developer experience. What is our "go build"?

Commands

chalet ...

init Initialize a project in either the current directory or a subdirectory.
configure Create a project configuration and fetch external dependencies.
build Run a valid executable build target.
rebuild Rebuild the project and create a configuration if it doesn't exist.
clean Unceremoniously clean the build folder.
buildrun Build a project and run a valid executable build target.
run Run a valid executable build target.
bundle Bundle a project for distribution.
export Export the project to another project format.
convert Convert the build file from one supported format to another. *

Build options

-t, --toolchain A toolchain or toolchain preset to use.
-a, --arch The architecture to target for the build.
-c, --configuration The build configuration to use.
-b --build-strategy The build strategy to use for the selected toolchain.
-j --max-jobs The number of jobs to run during compilation.

What's in a build file?


{
	"name": "new-project"
}

{
	"name": "new-project",
	"version": "0.0.1"
}

{
	"name": "new-project",
	"version": "0.0.1",
	"author": "John Doe",
	"description": "My first Chalet project",
	"license": "LICENSE.txt",
	"readme": "README.md",
	"homepage": "www.smallbiz.town"
}

{
	"name": "new-project",
	"version": "0.0.1"
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++"
		}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++",
			"files": [
				"src/main.cpp"
			]
		}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++",
			"files": "src/**.cpp"
		}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++",
			"files": "src/**.{cpp,rc}"
		}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++",
			"files": "src/**.{cpp,rc}",
			"settings:Cxx": {}
		}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++",
			"files": "src/**.{cpp,rc}",
			"settings:Cxx": {
				"cppStandard": "c++20"
			}
		}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++",
			"files": "src/**.{cpp,rc}",
			"settings:Cxx": {
				"cppStandard": "c++20",
				"warnings": [
					"all",
					"extra"
				]
			}
		}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++",
			"files": "src/**.{cpp,rc}",
			"settings:Cxx": {
				"cppStandard": "c++20",
				"warningsPreset": "pedantic"
			}
		}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++",
			"files": "src/**.{cpp,rc}",
			"settings:Cxx": {
				"cppStandard": "c++20",
				"warningsPreset": "pedantic",
				"precompiledHeader": "src/pch.hpp"
			}
		}
	}
}

{
	"name": "new-project",
	"version": "0.0.1",
	"targets": {
		"new-project": {
			"kind": "executable",
			"language": "C++",
			"files": "src/**.{cpp,rc}",
			"settings:Cxx": {
				"cppStandard": "c++20",
				"warningsPreset": "pedantic",
				"precompiledHeader": "src/pch.hpp",
				"includeDirs": [
					"src"
				]
			}
		}
	}
}

name: new-project
version: "0.0.1"
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      cppStandard: c++20
      warningsPreset: pedantic
      precompiledHeader: src/pch.hpp
      includeDirs:
        - src

name: new-project
version: "0.0.1"
defaultConfigurations:
  - Release
  - Debug
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      cppStandard: c++20
      warningsPreset: pedantic
      precompiledHeader: src/pch.hpp
      includeDirs:
        - src

name: new-project
version: "0.0.1"
defaultConfigurations:
  - Debug
configurations:
  Release:
    optimizationLevel: "3"
    debugSymbols: false
    interproceduralOptimization: true
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      cppStandard: c++20
      warningsPreset: pedantic
      precompiledHeader: src/pch.hpp
      includeDirs:
        - src

name: new-project
version: "0.0.1"
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      cppStandard: c++20
      warningsPreset: pedantic
      precompiledHeader: src/pch.hpp
      includeDirs:
        - src
      libDirs:
        - lib

name: new-project
version: "0.0.1"
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      cppStandard: c++20
      warningsPreset: pedantic
      precompiledHeader: src/pch.hpp
      includeDirs:
        - src
      libDirs:
        - lib
      links:
        - GL

name: new-project
version: "0.0.1"
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      cppStandard: c++20
      warningsPreset: pedantic
      precompiledHeader: src/pch.hpp
      includeDirs:
        - src
      libDirs:
        - lib
      links:
        - GL
      defines[:debug]:
        - DEBUG

name: new-project
version: "0.0.1"
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      cppStandard: c++20
      warningsPreset: pedantic
      precompiledHeader: src/pch.hpp
      includeDirs:
        - src
      libDirs:
        - lib
      links[:!windows]:
        - GL
      links[:windows]:
        - opengl32
      defines[:debug]:
        - DEBUG

name: new-project
version: "0.0.1"
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      cppStandard: c++20
      warningsPreset: pedantic
      precompiledHeader: src/pch.hpp
      includeDirs:
        - src
      libDirs:
        - lib
      links[:linux]:
        - GL
      links[:windows]:
        - opengl32
      appleFrameworks:
        - OpenGL
      defines[:debug]:
        - DEBUG

name: new-project
version: "0.0.1"
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      cppStandard: c++20
      warningsPreset: pedantic
      precompiledHeader: src/pch.hpp
      includeDirs:
        - src
      libDirs:
        - lib
      links[:linux]:
        - GL
      links[:windows]:
        - opengl32
      appleFrameworks:
        - OpenGL
      staticLinks[toolchain:mingw]:
        - prebuilt/mingw/libmylib.a
      defines[:debug]:
        - DEBUG

name: new-project
version: "0.0.1"
externalDependencies:
  glm:
    kind: git
    repository: git@github.com:g-truc/glm.git
    tag: "1.0.0"
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx: ...

name: new-project
version: "0.0.1"
externalDependencies:
  glm:
    kind: git
    repository: git@github.com:g-truc/glm.git
    tag: "1.0.0"
targets:
  glm:
    kind: cmakeProject
    location: ${external:glm}
    targets: glm/all
    defines:
      - BUILD_STATIC_LIBS=ON
      - BUILD_SHARED_LIBS=OFF
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx: ...

name: new-project
version: "0.0.1"
externalDependencies:
  glm:
    kind: git
    repository: git@github.com:g-truc/glm.git
    tag: "1.0.0"
package:
  glm.static:
    settings:Cxx:
      includeDirs: ${external:glm}
      staticLinks: ${externalBuild:glm}/glm/${ar:glm_static}
targets:
  glm:
    kind: cmakeProject
    location: ${external:glm}
    targets: glm/all
    defines: ...
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx: ...

name: new-project
version: "0.0.1"
externalDependencies:
  glm:
    kind: git
    repository: git@github.com:g-truc/glm.git
    tag: "1.0.0"
package:
  glm.static:
    settings:Cxx:
      includeDirs: ${external:glm}
      staticLinks: ${externalBuild:glm}/glm/${ar:glm_static}
targets:
  glm:
    kind: cmakeProject
    location: ${external:glm}
    targets: glm/all
    defines: ...
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx: ...
    importPackages:
      - glm.static

name: new-project
version: "0.0.1"
packagePaths:
  - /path/to/glm/chalet.yaml
targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx: ...
    importPackages:
      - glm.static

package:
  package1:
    settings:Cxx:
      includeDirs: include/package1
      staticLinks: ${buildDir}/${ar:package1}
  package2:
    importPackages:
      - package1
    settings:Cxx:
      includeDirs: include/package2
      staticLinks: ${buildDir}/${ar:package2}

packagePaths:
  - /path/to/package1/chalet.json
package:
  package2:
    importPackages:
      - package1
    settings:Cxx:
      includeDirs: include/package2
      staticLinks: ${buildDir}/${ar:package2}

targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      ...

targets:
  static-lib:
    kind: staticLibrary
    language: C++
    files: src/static/**.{cpp,rc}
    settings:Cxx: ...
  shared-lib:
    kind: sharedLibrary
    language: C++
    files: src/shared/**.{cpp,rc}
    settings:Cxx: ...
  new-project:
    kind: executable
    language: C++
    files: src/main/**.{cpp,rc}
    settings:Cxx:
      ...
      staticLinks: static-lib
      links: shared-lib

targets:
  chalet-target:
    kind: chaletProject
    location: /path/to/chalet.json
    targets:
      - project-a
  process-target:
    kind: process
    path: go
    arguments:
      - build
  script-target:
    kind: script
    file: scripts/script_python.py

targets:
  new-project:
    kind: executable
    language: C++
    files: src/**.{cpp,rc}
    settings:Cxx:
      ...
distribution:
  BundleName:
    kind: bundle
    buildTargets:
      - new-project
    include:
      - data
    includeDependentSharedLibraries: true

Live demo

Andrew King

Software developer - C++, Web & Games

Mastodon: @rewrking@mastodon.gamedev.place

Bluesky: @rewrking

Github: @rewrking

Linkedin: andrewraymondking