├── library ├── .gitignore ├── tests │ ├── src │ │ └── tests.cpp │ └── meson.build ├── include │ └── mylib.h ├── src │ └── library.c └── meson.build ├── executable ├── .gitignore ├── subprojects │ ├── mylib │ └── headeronly ├── src │ └── myexec.c └── meson.build ├── headeronly ├── .gitignore ├── tests │ ├── src │ │ └── tests.cpp │ └── meson.build ├── include │ └── headeronly.h └── meson.build ├── .gitmodules └── README.md /library/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /executable/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /headeronly/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /executable/subprojects/mylib: -------------------------------------------------------------------------------- 1 | ../../library -------------------------------------------------------------------------------- /executable/subprojects/headeronly: -------------------------------------------------------------------------------- 1 | ../../headeronly -------------------------------------------------------------------------------- /headeronly/tests/src/tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(HeaderOnly, function_returns_99) 5 | { 6 | EXPECT_EQ(99, headeronly_func_returning_99()); 7 | } 8 | -------------------------------------------------------------------------------- /headeronly/include/headeronly.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | static inline int headeronly_func_returning_99() 8 | { 9 | return 99; 10 | } 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "library/tests/googletest"] 2 | path = library/tests/googletest 3 | url = https://github.com/google/googletest.git 4 | [submodule "headeronly/tests/googletest"] 5 | path = headeronly/tests/googletest 6 | url = https://github.com/google/googletest.git 7 | -------------------------------------------------------------------------------- /library/tests/src/tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(MyLib, function_returns_0) 5 | { 6 | EXPECT_EQ(0, mylib_func_returning_0()); 7 | } 8 | 9 | TEST(MyLib, version_is_1_0_0) 10 | 11 | { 12 | EXPECT_STREQ("1.0.0", mylib_version()); 13 | } 14 | -------------------------------------------------------------------------------- /library/include/mylib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef MYLIB_PUBLIC 4 | #if defined _WIN32 || defined __CYGWIN__ 5 | #define MYLIB_PUBLIC __declspec(dllimport) 6 | #else 7 | #define MYLIB_PUBLIC 8 | #endif 9 | #endif 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | MYLIB_PUBLIC int mylib_func_returning_0(); 16 | 17 | MYLIB_PUBLIC const char* mylib_version(); 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /library/src/library.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define QUOTE(str) #str 4 | #define EXPAND_AND_QUOTE(str) QUOTE(str) 5 | 6 | 7 | /* This function will not be exported and is not 8 | * directly callable by users of this library. 9 | */ 10 | int internal_function() 11 | { 12 | return 0; 13 | } 14 | 15 | int mylib_func_returning_0() 16 | { 17 | return internal_function(); 18 | } 19 | 20 | const char* mylib_version() 21 | { 22 | return EXPAND_AND_QUOTE(PROJECT_VERSION); 23 | } 24 | -------------------------------------------------------------------------------- /executable/src/myexec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define QUOTE(str) #str 6 | #define EXPAND_AND_QUOTE(str) QUOTE(str) 7 | 8 | int main(int argc, char **argv) { 9 | if(argc != 1) { 10 | printf("%s takes no arguments.\n", argv[0]); 11 | return 1; 12 | } 13 | printf("This is project %s, version %s.\n", EXPAND_AND_QUOTE(PROJECT_NAME), EXPAND_AND_QUOTE(PROJECT_VERSION)); 14 | printf("mylib_func_returning_0() returns %d\n", mylib_func_returning_0()); 15 | printf("headeronly_func_returning_99() returns %d\n", headeronly_func_returning_99()); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /library/tests/meson.build: -------------------------------------------------------------------------------- 1 | # Builds google test as a dependency called "test_dep". 2 | 3 | gtest_dir = 'googletest/googletest' 4 | gtest_incdir = include_directories(join_paths(gtest_dir, 'include'), is_system : true) 5 | 6 | libgtest = static_library( 7 | 'gtest', 8 | cpp_args : ['-w'], 9 | include_directories : [include_directories(gtest_dir), gtest_incdir], 10 | sources : [ 11 | join_paths(gtest_dir, 'src', 'gtest-all.cc'), 12 | join_paths(gtest_dir, 'src', 'gtest_main.cc') 13 | ] 14 | ) 15 | 16 | test_dep = declare_dependency( 17 | dependencies : dependency('threads'), 18 | include_directories : gtest_incdir, 19 | link_with : libgtest 20 | ) 21 | -------------------------------------------------------------------------------- /headeronly/tests/meson.build: -------------------------------------------------------------------------------- 1 | # Builds google test as a dependency called "test_dep". 2 | 3 | gtest_dir = 'googletest/googletest' 4 | gtest_incdir = include_directories(join_paths(gtest_dir, 'include'), is_system : true) 5 | 6 | libgtest = static_library( 7 | 'gtest', 8 | cpp_args : ['-w'], 9 | include_directories : [include_directories(gtest_dir), gtest_incdir], 10 | sources : [ 11 | join_paths(gtest_dir, 'src', 'gtest-all.cc'), 12 | join_paths(gtest_dir, 'src', 'gtest_main.cc') 13 | ] 14 | ) 15 | 16 | test_dep = declare_dependency( 17 | dependencies : dependency('threads'), 18 | include_directories : gtest_incdir, 19 | link_with : libgtest 20 | ) 21 | -------------------------------------------------------------------------------- /executable/meson.build: -------------------------------------------------------------------------------- 1 | # Basic executable project. 2 | # 3 | # This project also uses the other two libraries as dependencies, 4 | # as seen in "project_dependencies". I'm using symlinks for the subprojects, 5 | # but ideally they should be subdirs of the subprojects dir, added via git 6 | # submodule. 7 | # 8 | # All of the commonly modified parts are above the line. Generally, you 9 | # shouldn't need to modify anything below the line until your project becomes 10 | # fairly complex. 11 | 12 | 13 | project( 14 | 'myexec', 15 | 'c', 16 | version : '1.1.0', 17 | default_options : ['warning_level=3'] 18 | ) 19 | 20 | project_source_files = [ 21 | 'src/myexec.c' 22 | ] 23 | 24 | project_dependencies = [ 25 | dependency('mylib', fallback : ['mylib', 'mylib_dep']), 26 | dependency('headeronly', fallback : ['headeronly', 'headeronly_dep']), 27 | ] 28 | 29 | build_args = [ 30 | ] 31 | 32 | 33 | # =================================================================== 34 | 35 | # ====== 36 | # Target 37 | # ====== 38 | 39 | build_args += [ 40 | '-DPROJECT_NAME=' + meson.project_name(), 41 | '-DPROJECT_VERSION=' + meson.project_version(), 42 | ] 43 | 44 | project_target = executable( 45 | meson.project_name(), 46 | project_source_files, 47 | dependencies: project_dependencies, 48 | install : true, 49 | c_args : build_args, 50 | ) 51 | 52 | test('basic', project_target) 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Meson Build File Examples 2 | ========================= 3 | 4 | [Meson](http://mesonbuild.com), like many projects, has documentation that is heavy on the detail, but weak on the big picture. There are no best practices offered, and questions like "How do I..." require a lot of digging around in the guts. 5 | 6 | The opinionated examples in this repository are designed as fully functional "jump start" projects to get you going: 7 | 8 | * Building a library 9 | * Building a header-only library 10 | * Building an executable 11 | * Setting up dependencies to other projects 12 | * Testing via google test 13 | 14 | 15 | ### Common Features 16 | 17 | * Project files are split such that they have a "simple config" section at the top for the most common things you'll want to modify. 18 | * Projects are structured in an opinionated way. 19 | * Public APIs are marked public or private (depending on their internal or external use) via `-DMYPROJECT_PUBLIC` defines, which handle windows and unix style visibility specifications. 20 | * The project name and version are passed to the compiler as `-DPROJECT_NAME` and `-DPROJECT_VERSION` 21 | * Project files are already set up to be used as dependencies of other projects, and as packages. 22 | * The library projects come with example tests. 23 | 24 | 25 | ### Building 26 | 27 | Requirements: 28 | 29 | * Meson 0.49 or newer 30 | * Ninja 1.8.2 or newer 31 | * A C compiler 32 | * A C++ compiler 33 | 34 | #### To build from command line, go into one of the project directories, then: 35 | 36 | To initialize the builder: 37 | 38 | meson build 39 | 40 | To build the project from then on: 41 | 42 | ninja -C build 43 | 44 | To run the tests: 45 | 46 | ninja -C build test 47 | 48 | To run the tests with full "google test" reporting: 49 | 50 | ./build/run_tests 51 | 52 | All build files will be put in the `build` subdir, which is included in `.gitignore`. 53 | 54 | 55 | ### Ideas? 56 | 57 | If you have other ideas for making these projects more robust as starting points, please open an issue to discuss it! 58 | -------------------------------------------------------------------------------- /headeronly/meson.build: -------------------------------------------------------------------------------- 1 | # Header-only library project. 2 | # 3 | # The main difference between this and a regular library project is that 4 | # there's no "link_with" parameter in project_dep (because there are no 5 | # sources to compile, thus no library to link with). 6 | # 7 | # All of the commonly modified parts are above the line. Generally, you 8 | # shouldn't need to modify anything below the line until your project becomes 9 | # fairly complex. 10 | 11 | 12 | project( 13 | 'headeronly', 14 | 'c', 15 | version : '2.0.0', 16 | default_options : ['warning_level=3'] 17 | ) 18 | project_description = 'An example header-only library' 19 | 20 | project_headers = [ 21 | 'include/headeronly.h' 22 | ] 23 | 24 | project_test_files = [ 25 | 'tests/src/tests.cpp', 26 | ] 27 | 28 | build_args = [ 29 | ] 30 | 31 | 32 | # =================================================================== 33 | 34 | # ====== 35 | # Target 36 | # ====== 37 | 38 | public_headers = include_directories('include') 39 | 40 | project_target = shared_library( 41 | meson.project_name(), 42 | [], 43 | install : true, 44 | c_args : build_args, 45 | gnu_symbol_visibility : 'hidden', 46 | include_directories : public_headers, 47 | ) 48 | 49 | 50 | # ======= 51 | # Project 52 | # ======= 53 | 54 | # Make this library usable as a Meson subproject. 55 | project_dep = declare_dependency( 56 | include_directories: public_headers 57 | ) 58 | set_variable(meson.project_name() + '_dep', project_dep) 59 | 60 | # Make this library usable from the system's 61 | # package manager. 62 | install_headers(project_headers, subdir : meson.project_name()) 63 | 64 | pkg_mod = import('pkgconfig') 65 | pkg_mod.generate( 66 | name : meson.project_name(), 67 | filebase : meson.project_name(), 68 | description : project_description, 69 | subdirs : meson.project_name(), 70 | ) 71 | 72 | 73 | # ========== 74 | # Unit Tests 75 | # ========== 76 | 77 | if not meson.is_subproject() 78 | add_languages('cpp') 79 | subdir('tests') 80 | 81 | test('all_tests', 82 | executable( 83 | 'run_tests', 84 | files(project_test_files), 85 | dependencies : [project_dep, test_dep], 86 | install : false 87 | ) 88 | ) 89 | endif 90 | -------------------------------------------------------------------------------- /library/meson.build: -------------------------------------------------------------------------------- 1 | # Basic library project. 2 | # 3 | # All of the commonly modified parts are above the line. Generally, you 4 | # shouldn't need to modify anything below the line until your project becomes 5 | # fairly complex. 6 | 7 | 8 | project( 9 | 'mylib', 10 | 'c', 11 | version : '1.0.0', 12 | default_options : ['warning_level=3'] 13 | ) 14 | project_description = 'An example shared library' 15 | 16 | project_headers = [ 17 | 'include/mylib.h' 18 | ] 19 | 20 | project_source_files = [ 21 | 'src/library.c' 22 | ] 23 | 24 | project_test_files = [ 25 | 'tests/src/tests.cpp', 26 | ] 27 | 28 | build_args = [ 29 | ] 30 | 31 | 32 | # =================================================================== 33 | 34 | # ====== 35 | # Target 36 | # ====== 37 | 38 | public_headers = include_directories('include') 39 | 40 | build_args += [ 41 | '-DPROJECT_NAME=' + meson.project_name(), 42 | '-DPROJECT_VERSION=' + meson.project_version(), 43 | ] 44 | 45 | # Only make public interfaces visible 46 | if target_machine.system() == 'windows' or target_machine.system() == 'cygwin' 47 | build_args += '-DMYLIB_PUBLIC="__declspec(dllexport)"' 48 | else 49 | build_args += '-DMYLIB_PUBLIC=__attribute__((visibility("default")))' 50 | endif 51 | 52 | project_target = shared_library( 53 | meson.project_name(), 54 | project_source_files, 55 | install : true, 56 | c_args : build_args, 57 | gnu_symbol_visibility : 'hidden', 58 | include_directories : public_headers, 59 | ) 60 | 61 | 62 | # ======= 63 | # Project 64 | # ======= 65 | 66 | # Make this library usable as a Meson subproject. 67 | project_dep = declare_dependency( 68 | include_directories: public_headers, 69 | link_with : project_target 70 | ) 71 | set_variable(meson.project_name() + '_dep', project_dep) 72 | 73 | # Make this library usable from the system's 74 | # package manager. 75 | install_headers(project_headers, subdir : meson.project_name()) 76 | 77 | pkg_mod = import('pkgconfig') 78 | pkg_mod.generate( 79 | name : meson.project_name(), 80 | filebase : meson.project_name(), 81 | description : project_description, 82 | subdirs : meson.project_name(), 83 | libraries : project_target, 84 | ) 85 | 86 | 87 | # ========== 88 | # Unit Tests 89 | # ========== 90 | 91 | if not meson.is_subproject() 92 | add_languages('cpp') 93 | subdir('tests') 94 | 95 | test('all_tests', 96 | executable( 97 | 'run_tests', 98 | files(project_test_files), 99 | dependencies : [project_dep, test_dep], 100 | install : false 101 | ) 102 | ) 103 | endif 104 | --------------------------------------------------------------------------------