From d2b68d31dd630e5aeacc3588151e80e6774b51bf Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 22 Dec 2017 20:33:21 +0200 Subject: [PATCH 1/6] Added init command that creates a sample exe project. --- mesonbuild/mesonmain.py | 4 ++- mesonbuild/minit.py | 64 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 mesonbuild/minit.py diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 20ec3045c..12bbd6914 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -17,7 +17,7 @@ import time, datetime import os.path from . import environment, interpreter, mesonlib from . import build -from . import mconf, mintro, mtest, rewriter +from . import mconf, mintro, mtest, rewriter, minit import platform from . import mlog, coredata from .mesonlib import MesonException @@ -308,6 +308,8 @@ def run(original_args, mainfile=None): sys.exit(1) elif cmd_name == 'wrap': return wraptool.run(remaining_args) + elif cmd_name == 'init': + return minit.run(remaining_args) elif cmd_name == 'runpython': import runpy script_file = remaining_args[0] diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py new file mode 100644 index 000000000..51c8b2009 --- /dev/null +++ b/mesonbuild/minit.py @@ -0,0 +1,64 @@ +# Copyright 2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Code that creates simple startup projects.""" + +import os, sys, argparse, re +from glob import glob + +hello_c_template = '''#include + +#define PROJECT_NAME "{project_name}" + +int main(int argc, char **argv) {{ + printf("This is project %s.", PROJECT_NAME); + return 0; +}} +''' + +hello_c_meson_template = '''project('{project_name}', 'c') + +executable('{exe_name}', '{source_name}', + install : true) +''' + +info_message = '''Sample project created. To build it run the +following commands: + +meson builddir +ninja -C builddir +''' + +def create_exe_c_sample(project_name): + lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) + uppercase_token = lowercase_token.upper() + source_name = lowercase_token + '.c' + open(source_name, 'w').write(hello_c_template.format(project_name=project_name)) + open('meson.build', 'w').write(hello_c_meson_template.format(project_name=project_name, + exe_name=lowercase_token, + source_name=source_name)) + +def create_sample(options): + create_exe_c_sample(options.name) + print(info_message) + +def run(args): + parser = argparse.ArgumentParser(prog='meson') + parser.add_argument('--name', default = 'mesonsample') + #parser.add_argument('--type', default_value='executable') + options = parser.parse_args(args) + if len(glob('*')) != 0: + sys.exit('This command must be run in an empty directory.') + create_sample(options) + return 0 From 3cc1462dd24f373ec4cf047564ff0d77f4b6d3e9 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 22 Dec 2017 21:01:02 +0200 Subject: [PATCH 2/6] Add a sample shared library project. --- mesonbuild/minit.py | 133 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 4 deletions(-) diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py index 51c8b2009..a98ed67b5 100644 --- a/mesonbuild/minit.py +++ b/mesonbuild/minit.py @@ -1,4 +1,5 @@ # Copyright 2017 The Meson development team +from pyclbr import Function # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,20 +18,112 @@ import os, sys, argparse, re from glob import glob +lib_h_template = '''#pragma once +#if defined _WIN32 || defined __CYGWIN__ + #ifdef BUILDING_{utoken} + #define {utoken}_PUBLIC __attribute__ ((dllexport)) + #else + #define {utoken}_PUBLIC __attribute__ ((dllimport)) + #endif +#else + #define {utoken}_PUBLIC __attribute__ ((visibility ("default"))) +#endif + +int {utoken}_PUBLIC {function_name}(); + +''' + +lib_c_template = '''#include <{header_file}> + +/* This function will not be exported and is not + * directly callable by users of this library. + */ +int internal_function() {{ + return 0; +}} + +int {function_name}() {{ + return internal_function(); +}} +''' + +lib_c_test_template = '''#include <{header_file}> +#include + +int main(int argc, char **argv) {{ + if(argc != 1) {{ + printf("%s takes no arguments.\\n", argv[0]); + return 1; + }} + return {function_name}(); +}} +''' + +lib_c_meson_template = '''project('{project_name}', 'c', + version : '{version}', + default_options : ['warning_level=3']) + +# These arguments are only used to build the shared library +# not the executables that use the library. +lib_args = ['-DBUILDING_{utoken}'] + +# Hiding symbols that are not explicitly marked as exported +# requires a compiler flag on all compilers except VS. +cc = meson.get_compiler('c') +if cc.get_id() != 'msvc' + lib_args += ['-fvisibility=hidden'] +endif + +shlib = shared_library('{lib_name}', '{source_file}', + install : true, + c_args : lib_args, +) + +test_exe = executable('{test_exe_name}', '{test_source_file}', + link_with : shlib) +test('{test_name}', test_exe) + +# Make this library usable as a Meson subproject. +{ltoken}_dep = declare_dependency( + include_directories: include_directories('.'), + link_with : shlib) + +# Make this library usable from the system's +# package manager. +install_headers('{header_file}', subdir : '{header_dir}') + +pkg_mod = import('pkgconfig') +pkg_mod.generate( + name : '{project_name}', + filebase : '{ltoken}', + description : 'Meson sample project.', + subdirs : '{header_dir}', + libraries : shlib, + version : '{version}', +) +''' + hello_c_template = '''#include #define PROJECT_NAME "{project_name}" int main(int argc, char **argv) {{ + if(argc != 1) {{ + printf("%s takes not arguments.\\n", argv[0]); + return 1; + }} printf("This is project %s.", PROJECT_NAME); return 0; }} ''' -hello_c_meson_template = '''project('{project_name}', 'c') +hello_c_meson_template = '''project('{project_name}', 'c', + default_options : ['warning_level=3']) -executable('{exe_name}', '{source_name}', +exe = executable('{exe_name}', '{source_name}', install : true) + +test('basic', exe) ''' info_message = '''Sample project created. To build it run the @@ -49,14 +142,46 @@ def create_exe_c_sample(project_name): exe_name=lowercase_token, source_name=source_name)) +def create_lib_c_sample(project_name, version): + lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) + uppercase_token = lowercase_token.upper() + function_name = lowercase_token[0:3] + '_func' + lib_h_name = lowercase_token + '.h' + lib_c_name = lowercase_token + '.c' + test_c_name = lowercase_token + '_test.c' + kwargs = {'utoken': uppercase_token, + 'ltoken': lowercase_token, + 'header_dir': lowercase_token, + 'function_name': function_name, + 'header_file': lib_h_name, + 'source_file': lib_c_name, + 'test_source_file': test_c_name, + 'test_exe_name': lowercase_token, + 'project_name': project_name, + 'lib_name': lowercase_token, + 'test_name': lowercase_token, + 'version': version, + } + open(lib_h_name, 'w').write(lib_h_template.format(**kwargs)) + open(lib_c_name, 'w').write(lib_c_template.format(**kwargs)) + open(test_c_name, 'w').write(lib_c_test_template.format(**kwargs)) + open('meson.build', 'w').write(lib_c_meson_template.format(**kwargs)) + def create_sample(options): - create_exe_c_sample(options.name) + if options.type == 'executable': + create_exe_c_sample(options.name) + elif options.type == 'library': + create_lib_c_sample(options.name, options.version) + else: + raise RuntimeError('Unreachable code') print(info_message) def run(args): parser = argparse.ArgumentParser(prog='meson') parser.add_argument('--name', default = 'mesonsample') - #parser.add_argument('--type', default_value='executable') + parser.add_argument('--type', default='executable', + choices=['executable', 'library']) + parser.add_argument('--version', default='1.0') options = parser.parse_args(args) if len(glob('*')) != 0: sys.exit('This command must be run in an empty directory.') From 82c8550fb6f124835726789b13bbc37746e16bcf Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 22 Dec 2017 21:51:03 +0200 Subject: [PATCH 3/6] Added documentation for project templates. --- docs/markdown/Project-templates.md | 28 ++++++++++++++++++++++++++++ docs/sitemap.txt | 1 + 2 files changed, 29 insertions(+) create mode 100644 docs/markdown/Project-templates.md diff --git a/docs/markdown/Project-templates.md b/docs/markdown/Project-templates.md new file mode 100644 index 000000000..dd86e13db --- /dev/null +++ b/docs/markdown/Project-templates.md @@ -0,0 +1,28 @@ +--- +short-description: Project templates +... + +# Project templates (available since 0.45.0) + +To make it easier for new developers to start working, Meson ships a +tool to generate the basic setup of different kinds of projects. This +functionality can be accessed with the `meson init` command. A typical +project setup would go like this: + +```console +$ mkdir project_name +$ cd project_name +$ meson init --language=c --name=myproject --version=0.1 +``` + +This would create the build definitions for a helloworld type +project. The result can be compiled as usual. For example compiling it +with Ninja could be done like this: + +``` +$ meson builddir +$ ninja -C builddir +``` + +The generator has many different projects and settings. They can all +be listed by invoking the command `meson test --help`. diff --git a/docs/sitemap.txt b/docs/sitemap.txt index b7ee136ca..87a5eb51f 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -48,6 +48,7 @@ index.md Creating-releases.md Creating-OSX-packages.md Creating-Linux-binaries.md + Project-templates.md Reference-manual.md Reference-tables.md FAQ.md From 21101fd00affed032619993b5374ccc1cced5c37 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Wed, 27 Dec 2017 23:16:48 +0200 Subject: [PATCH 4/6] Added C++ exe sample project. --- mesonbuild/minit.py | 59 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py index a98ed67b5..4ccd590ff 100644 --- a/mesonbuild/minit.py +++ b/mesonbuild/minit.py @@ -112,12 +112,37 @@ int main(int argc, char **argv) {{ printf("%s takes not arguments.\\n", argv[0]); return 1; }} - printf("This is project %s.", PROJECT_NAME); + printf("This is project %s.\n", PROJECT_NAME); return 0; }} ''' hello_c_meson_template = '''project('{project_name}', 'c', + version : '{version}', + default_options : ['warning_level=3']) + +exe = executable('{exe_name}', '{source_name}', + install : true) + +test('basic', exe) +''' + +hello_cpp_template = '''#include + +#define PROJECT_NAME "{project_name}" + +int main(int argc, char **argv) {{ + if(argc != 1) {{ + printf("%s takes not arguments.\\n", argv[0]); + return 1; + }} + std::cout << "This is project " << PROJECT_NAME << ".\\n"; + return 0; +}} +''' + +hello_cpp_meson_template = '''project('{project_name}', 'cpp', + version : '{version}', default_options : ['warning_level=3']) exe = executable('{exe_name}', '{source_name}', @@ -133,14 +158,15 @@ meson builddir ninja -C builddir ''' -def create_exe_c_sample(project_name): +def create_exe_c_sample(project_name, project_version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) uppercase_token = lowercase_token.upper() source_name = lowercase_token + '.c' open(source_name, 'w').write(hello_c_template.format(project_name=project_name)) open('meson.build', 'w').write(hello_c_meson_template.format(project_name=project_name, exe_name=lowercase_token, - source_name=source_name)) + source_name=source_name, + version=project_version)) def create_lib_c_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) @@ -167,11 +193,29 @@ def create_lib_c_sample(project_name, version): open(test_c_name, 'w').write(lib_c_test_template.format(**kwargs)) open('meson.build', 'w').write(lib_c_meson_template.format(**kwargs)) +def create_exe_cpp_sample(project_name, project_version): + lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) + uppercase_token = lowercase_token.upper() + source_name = lowercase_token + '.cpp' + open(source_name, 'w').write(hello_cpp_template.format(project_name=project_name)) + open('meson.build', 'w').write(hello_cpp_meson_template.format(project_name=project_name, + exe_name=lowercase_token, + source_name=source_name, + version=project_version)) + def create_sample(options): - if options.type == 'executable': - create_exe_c_sample(options.name) - elif options.type == 'library': - create_lib_c_sample(options.name, options.version) + if options.language == 'c': + if options.type == 'executable': + create_exe_c_sample(options.name, options.version) + elif options.type == 'library': + create_lib_c_sample(options.name, options.version) + else: + raise RuntimeError('Unreachable code') + elif options.language == 'cpp': + if options.type == 'executable': + create_exe_cpp_sample(options.name, options.version) + else: + raise RuntimeError('Unreachable code') else: raise RuntimeError('Unreachable code') print(info_message) @@ -181,6 +225,7 @@ def run(args): parser.add_argument('--name', default = 'mesonsample') parser.add_argument('--type', default='executable', choices=['executable', 'library']) + parser.add_argument('--language', default='c', choices=['c', 'cpp']) parser.add_argument('--version', default='1.0') options = parser.parse_args(args) if len(glob('*')) != 0: From f1afd44b2b2fb85de1c55408b46e50b4381f50d2 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Wed, 27 Dec 2017 23:40:15 +0200 Subject: [PATCH 5/6] Added C++ library sample. --- mesonbuild/minit.py | 147 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 143 insertions(+), 4 deletions(-) diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py index 4ccd590ff..29b5056e6 100644 --- a/mesonbuild/minit.py +++ b/mesonbuild/minit.py @@ -21,12 +21,16 @@ from glob import glob lib_h_template = '''#pragma once #if defined _WIN32 || defined __CYGWIN__ #ifdef BUILDING_{utoken} - #define {utoken}_PUBLIC __attribute__ ((dllexport)) + #define {utoken}_PUBLIC __declspec(dllexport) #else - #define {utoken}_PUBLIC __attribute__ ((dllimport)) + #define {utoken}_PUBLIC __declspec(dllimport) #endif #else - #define {utoken}_PUBLIC __attribute__ ((visibility ("default"))) + #ifdef BUILDING_{utoken} + #define {utoken}_PUBLIC __attribute__ ((visibility ("default"))) + #else + #define {utoken}_PUBLIC + #endif #endif int {utoken}_PUBLIC {function_name}(); @@ -119,7 +123,8 @@ int main(int argc, char **argv) {{ hello_c_meson_template = '''project('{project_name}', 'c', version : '{version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3', + 'cpp_std=c++14']) exe = executable('{exe_name}', '{source_name}', install : true) @@ -151,6 +156,111 @@ exe = executable('{exe_name}', '{source_name}', test('basic', exe) ''' +lib_hpp_template = '''#pragma once +#if defined _WIN32 || defined __CYGWIN__ + #ifdef BUILDING_{utoken} + #define {utoken}_PUBLIC __declspec(dllexport) + #else + #define {utoken}_PUBLIC __declspec(dllimport) + #endif +#else + #ifdef BUILDING_{utoken} + #define {utoken}_PUBLIC __attribute__ ((visibility ("default"))) + #else + #define {utoken}_PUBLIC + #endif +#endif + +namespace {namespace} {{ + +class {utoken}_PUBLIC {class_name} {{ + +public: + {class_name}(); + int get_number() const; + +private: + + int number; + +}}; + +}} + +''' + +lib_cpp_template = '''#include <{header_file}> + +namespace {namespace} {{ + +{class_name}::{class_name}() {{ + number = 6; +}} + +int {class_name}::get_number() const {{ + return number; +}} + +}} +''' + +lib_cpp_test_template = '''#include <{header_file}> +#include + +int main(int argc, char **argv) {{ + if(argc != 1) {{ + printf("%s takes no arguments.\\n", argv[0]); + return 1; + }} + {namespace}::{class_name} c; + return c.get_number() != 6; +}} +''' + +lib_cpp_meson_template = '''project('{project_name}', 'cpp', + version : '{version}', + default_options : ['warning_level=3', 'cpp_std=c++14']) + +# These arguments are only used to build the shared library +# not the executables that use the library. +lib_args = ['-DBUILDING_{utoken}'] + +# Hiding symbols that are not explicitly marked as exported +# requires a compiler flag on all compilers except VS. +cpp = meson.get_compiler('cpp') +if cpp.get_id() != 'msvc' + lib_args += ['-fvisibility=hidden'] +endif + +shlib = shared_library('{lib_name}', '{source_file}', + install : true, + cpp_args : lib_args, +) + +test_exe = executable('{test_exe_name}', '{test_source_file}', + link_with : shlib) +test('{test_name}', test_exe) + +# Make this library usable as a Meson subproject. +{ltoken}_dep = declare_dependency( + include_directories: include_directories('.'), + link_with : shlib) + +# Make this library usable from the system's +# package manager. +install_headers('{header_file}', subdir : '{header_dir}') + +pkg_mod = import('pkgconfig') +pkg_mod.generate( + name : '{project_name}', + filebase : '{ltoken}', + description : 'Meson sample project.', + subdirs : '{header_dir}', + libraries : shlib, + version : '{version}', +) +''' + info_message = '''Sample project created. To build it run the following commands: @@ -203,6 +313,33 @@ def create_exe_cpp_sample(project_name, project_version): source_name=source_name, version=project_version)) +def create_lib_cpp_sample(project_name, version): + lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) + uppercase_token = lowercase_token.upper() + class_name = uppercase_token[0] + lowercase_token[1:] + namespace = lowercase_token + lib_h_name = lowercase_token + '.hpp' + lib_c_name = lowercase_token + '.cpp' + test_c_name = lowercase_token + '_test.cpp' + kwargs = {'utoken': uppercase_token, + 'ltoken': lowercase_token, + 'header_dir': lowercase_token, + 'class_name': class_name, + 'namespace': namespace, + 'header_file': lib_h_name, + 'source_file': lib_c_name, + 'test_source_file': test_c_name, + 'test_exe_name': lowercase_token, + 'project_name': project_name, + 'lib_name': lowercase_token, + 'test_name': lowercase_token, + 'version': version, + } + open(lib_h_name, 'w').write(lib_hpp_template.format(**kwargs)) + open(lib_c_name, 'w').write(lib_cpp_template.format(**kwargs)) + open(test_c_name, 'w').write(lib_cpp_test_template.format(**kwargs)) + open('meson.build', 'w').write(lib_cpp_meson_template.format(**kwargs)) + def create_sample(options): if options.language == 'c': if options.type == 'executable': @@ -214,6 +351,8 @@ def create_sample(options): elif options.language == 'cpp': if options.type == 'executable': create_exe_cpp_sample(options.name, options.version) + elif options.type == 'library': + create_lib_cpp_sample(options.name, options.version) else: raise RuntimeError('Unreachable code') else: From 4a189cf8c53e22b747521cae93c18cac3a45cc5a Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Thu, 28 Dec 2017 00:01:31 +0200 Subject: [PATCH 6/6] Add unit test that checks that the sample projects compile. --- docs/markdown/Project-templates.md | 4 +++- docs/markdown/snippets/templates.md | 8 ++++++++ mesonbuild/minit.py | 8 ++++---- run_unittests.py | 15 ++++++++++++++- 4 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 docs/markdown/snippets/templates.md diff --git a/docs/markdown/Project-templates.md b/docs/markdown/Project-templates.md index dd86e13db..d8459c6e6 100644 --- a/docs/markdown/Project-templates.md +++ b/docs/markdown/Project-templates.md @@ -2,7 +2,7 @@ short-description: Project templates ... -# Project templates (available since 0.45.0) +# Project templates To make it easier for new developers to start working, Meson ships a tool to generate the basic setup of different kinds of projects. This @@ -26,3 +26,5 @@ $ ninja -C builddir The generator has many different projects and settings. They can all be listed by invoking the command `meson test --help`. + +This feature is available since Meson version 0.45.0. diff --git a/docs/markdown/snippets/templates.md b/docs/markdown/snippets/templates.md new file mode 100644 index 000000000..6f0474d8d --- /dev/null +++ b/docs/markdown/snippets/templates.md @@ -0,0 +1,8 @@ +## Project templates + +Meson ships with predefined project templates. To start a new project from +scratch, simply go to an empty directory and type: + +```meson +meson init --name=myproject --type=executable --language=c +``` diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py index 29b5056e6..98817cba4 100644 --- a/mesonbuild/minit.py +++ b/mesonbuild/minit.py @@ -113,10 +113,10 @@ hello_c_template = '''#include int main(int argc, char **argv) {{ if(argc != 1) {{ - printf("%s takes not arguments.\\n", argv[0]); + printf("%s takes no arguments.\\n", argv[0]); return 1; }} - printf("This is project %s.\n", PROJECT_NAME); + printf("This is project %s.\\n", PROJECT_NAME); return 0; }} ''' @@ -138,7 +138,7 @@ hello_cpp_template = '''#include int main(int argc, char **argv) {{ if(argc != 1) {{ - printf("%s takes not arguments.\\n", argv[0]); + std::cout << argv[0] << "takes no arguments.\\n"; return 1; }} std::cout << "This is project " << PROJECT_NAME << ".\\n"; @@ -209,7 +209,7 @@ lib_cpp_test_template = '''#include <{header_file}> int main(int argc, char **argv) {{ if(argc != 1) {{ - printf("%s takes no arguments.\\n", argv[0]); + std::cout << argv[0] << " takes no arguments.\\n"; return 1; }} {namespace}::{class_name} c; diff --git a/run_unittests.py b/run_unittests.py index 184386cb7..cbdcda076 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -36,7 +36,7 @@ import mesonbuild.coredata from mesonbuild.interpreter import ObjectHolder from mesonbuild.mesonlib import is_linux, is_windows, is_osx, is_cygwin, windows_proof_rmtree from mesonbuild.mesonlib import python_command, meson_command, version_compare -from mesonbuild.environment import Environment +from mesonbuild.environment import Environment, detect_ninja from mesonbuild.mesonlib import MesonException, EnvironmentException from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram @@ -1707,6 +1707,19 @@ int main(int argc, char **argv) { self.init(workdir) self.build() + def test_templates(self): + ninja = detect_ninja() + if ninja is None: + raise unittest.SkipTest('This test currently requires ninja. Fix this once "meson build" works.') + for lang in ('c', 'cpp'): + for type in ('executable', 'library'): + with tempfile.TemporaryDirectory() as tmpdir: + self._run(meson_command + ['init', '--language', lang, '--type', type], + workdir=tmpdir) + self._run(self.meson_command + ['--backend=ninja', 'builddir'], + workdir=tmpdir) + self._run(ninja, + workdir=os.path.join(tmpdir, 'builddir')) class FailureTests(BasePlatformTests): '''