diff options
Diffstat (limited to 'libbuild2/c')
-rw-r--r-- | libbuild2/c/buildfile | 90 | ||||
-rw-r--r-- | libbuild2/c/export.hxx | 38 | ||||
-rw-r--r-- | libbuild2/c/init.cxx | 375 | ||||
-rw-r--r-- | libbuild2/c/init.hxx | 32 | ||||
-rw-r--r-- | libbuild2/c/target.hxx | 22 |
5 files changed, 557 insertions, 0 deletions
diff --git a/libbuild2/c/buildfile b/libbuild2/c/buildfile new file mode 100644 index 0000000..39a5d7a --- /dev/null +++ b/libbuild2/c/buildfile @@ -0,0 +1,90 @@ +# file : libbuild2/c/buildfile +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +import int_libs = libbutl%lib{butl} + +include ../ +int_libs += ../lib{build2} + +include ../cc/ +int_libs += ../cc/lib{build2-cc} + +./: lib{build2-c}: libul{build2-c}: {hxx ixx txx cxx}{** -**.test...} \ + $int_libs + +# Unit tests. +# +exe{*.test}: +{ + test = true + install = false +} + +for t: cxx{**.test...} +{ + d = $directory($t) + n = $name($t)... + + ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} + $d/exe{$n}: libul{build2-c}: bin.whole = false +} + +# Build options. +# +obja{*}: cxx.poptions += -DLIBBUILD2_C_STATIC_BUILD +objs{*}: cxx.poptions += -DLIBBUILD2_C_SHARED_BUILD + +# Note that we used to compare complete target triplets but that proved too +# strict (see libbuild2/buildfile for details). +# +cross = ($cxx.target.cpu != $build.host.cpu || \ + $cxx.target.system != $build.host.system) + +# Pass native C compiler path (not forgetting to escape backslashes on +# Windows). It is used as defaults for BUILD2_DEFAULT_C. +# +if! $cross +{ + obj{init}: cxx.poptions += \ + -DBUILD2_NATIVE_C=\"$regex.replace($recall($c.path), '\\', '\\\\')\" + + obja{init}: cxx.poptions += -DLIBBUILD2_C_STATIC_BUILD + objs{init}: cxx.poptions += -DLIBBUILD2_C_SHARED_BUILD +} + +# Export options. +# +lib{build2-c}: +{ + cxx.export.poptions = "-I$out_root" "-I$src_root" + cxx.export.libs = $int_libs +} + +liba{build2-c}: cxx.export.poptions += -DLIBBUILD2_C_STATIC +libs{build2-c}: cxx.export.poptions += -DLIBBUILD2_C_SHARED + +# For pre-releases use the complete version to make sure they cannot be used +# in place of another pre-release or the final version. See the version module +# for details on the version.* variable values. +# +# And because this is a build system module, we also embed the same value as +# the interface version (note that we cannot use build.version.interface for +# bundled modules because we could be built with a different version of the +# build system). +# +ver = ($version.pre_release \ + ? "$version.project_id" \ + : "$version.major.$version.minor") + +lib{build2-c}: bin.lib.version = @"-$ver" +libs{build2-c}: bin.lib.load_suffix = "-$ver" + +# Install into the libbuild2/c/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/libbuild2/c/ + install.subdirs = true +} diff --git a/libbuild2/c/export.hxx b/libbuild2/c/export.hxx new file mode 100644 index 0000000..6fe3203 --- /dev/null +++ b/libbuild2/c/export.hxx @@ -0,0 +1,38 @@ +// file : libbuild2/c/export.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#pragma once + +// Normally we don't export class templates (but do complete specializations), +// inline functions, and classes with only inline member functions. Exporting +// classes that inherit from non-exported/imported bases (e.g., std::string) +// will end up badly. The only known workarounds are to not inherit or to not +// export. Also, MinGW GCC doesn't like seeing non-exported functions being +// used before their inline definition. The workaround is to reorder code. In +// the end it's all trial and error. + +#if defined(LIBBUILD2_C_STATIC) // Using static. +# define LIBBUILD2_C_SYMEXPORT +#elif defined(LIBBUILD2_C_STATIC_BUILD) // Building static. +# define LIBBUILD2_C_SYMEXPORT +#elif defined(LIBBUILD2_C_SHARED) // Using shared. +# ifdef _WIN32 +# define LIBBUILD2_C_SYMEXPORT __declspec(dllimport) +# else +# define LIBBUILD2_C_SYMEXPORT +# endif +#elif defined(LIBBUILD2_C_SHARED_BUILD) // Building shared. +# ifdef _WIN32 +# define LIBBUILD2_C_SYMEXPORT __declspec(dllexport) +# else +# define LIBBUILD2_C_SYMEXPORT +# endif +#else +// If none of the above macros are defined, then we assume we are being used +// by some third-party build system that cannot/doesn't signal the library +// type. Note that this fallback works for both static and shared but in case +// of shared will be sub-optimal compared to having dllimport. +// +# define LIBBUILD2_C_SYMEXPORT // Using static or shared. +#endif diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx new file mode 100644 index 0000000..43d647f --- /dev/null +++ b/libbuild2/c/init.cxx @@ -0,0 +1,375 @@ +// file : libbuild2/c/init.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <libbuild2/c/init.hxx> + +#include <libbuild2/scope.hxx> +#include <libbuild2/diagnostics.hxx> + +#include <libbuild2/cc/guess.hxx> +#include <libbuild2/cc/module.hxx> + +#include <libbuild2/c/target.hxx> + +#ifndef BUILD2_DEFAULT_C +# ifdef BUILD2_NATIVE_C +# define BUILD2_DEFAULT_C BUILD2_NATIVE_C +# else +# define BUILD2_DEFAULT_C "" +# endif +#endif + +using namespace std; +using namespace butl; + +namespace build2 +{ + namespace c + { + using cc::compiler_id; + using cc::compiler_class; + using cc::compiler_info; + + class config_module: public cc::config_module + { + public: + explicit + config_module (config_data&& d) + : config_data (move (d)), cc::config_module (move (d)) {} + + virtual strings + translate_std (const compiler_info&, + scope&, + const string*) const override; + }; + + using cc::module; + + strings config_module:: + translate_std (const compiler_info& ci, scope& rs, const string* v) const + { + strings r; + + switch (ci.class_) + { + case compiler_class::msvc: + { + // Standard-wise, with VC you get what you get. The question is + // whether we should verify that the requested standard is provided + // by this VC version. And if so, from which version should we say + // VC supports 90, 99, and 11? We should probably be as loose as + // possible here since the author will always be able to tighten + // (but not loosen) this in the buildfile (i.e., detect unsupported + // versions). + // + // The state of affairs seem to be (from Herb Sutter's blog): + // + // 10.0 - most of C95 plus a few C99 features + // 11.0 - partial support for the C++11 subset of C11 + // 12.0 - more C11 features from the C++11 subset, most of C99 + // + // So let's say C99 is supported from 10.0 and C11 from 11.0. And + // C90 is supported by everything we care to support. + // + // C17 is a bug-fix version of C11 so here we assume it is the same + // as C11. + // + if (v == nullptr) + ; + else if (*v != "90") + { + uint64_t cver (ci.version.major); + + if ((*v == "99" && cver < 16) || // Since VS2010/10.0. + ((*v == "11" || + *v == "17") && cver < 17)) // Since VS2012/11.0. + { + fail << "C" << *v << " is not supported by " << ci.signature << + info << "required by " << project (rs) << '@' << rs; + } + } + break; + } + case compiler_class::gcc: + { + // 90 and 89 are the same standard. Translate 99 to 9x and 11 to 1x + // for compatibility with older versions of the compilers. + // + if (v == nullptr) + ; + else + { + string o ("-std="); + + if (*v == "90") o += "c90"; + else if (*v == "99") o += "c9x"; + else if (*v == "11") o += "c1x"; + else if (*v == "17") o += "c17"; // GCC 8, Clang 6. + else o += *v; // In case the user specifies e.g., 'gnu11'. + + r.push_back (move (o)); + } + break; + } + } + + return r; + } + + static const char* const hinters[] = {"cxx", nullptr}; + + // See cc::module for details on guess_init vs config_init. + // + bool + guess_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr<module_base>& mod, + bool, + bool, + const variable_map& hints) + { + tracer trace ("c::guess_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "c.guess module must be loaded in project root"; + + // Load cc.core.vars so that we can cache all the cc.* variables. + // + if (!cast_false<bool> (rs["cc.core.vars.loaded"])) + load_module (rs, rs, "cc.core.vars", loc); + + // Enter all the variables and initialize the module data. + // + auto& v (rs.ctx.var_pool.rw (rs)); + + cc::config_data d { + cc::lang::c, + + "c", + "c", + BUILD2_DEFAULT_C, + ".i", + + hinters, + + // Note: some overridable, some not. + // + v.insert<path> ("config.c", true), + v.insert<string> ("config.c.id", true), + v.insert<string> ("config.c.version", true), + v.insert<string> ("config.c.target", true), + v.insert<string> ("config.c.std", true), + v.insert<strings> ("config.c.poptions", true), + v.insert<strings> ("config.c.coptions", true), + v.insert<strings> ("config.c.loptions", true), + v.insert<strings> ("config.c.aoptions", true), + v.insert<strings> ("config.c.libs", true), + nullptr /* config.c.importable_headers */, + + v.insert<process_path> ("c.path"), + v.insert<dir_paths> ("c.sys_lib_dirs"), + v.insert<dir_paths> ("c.sys_inc_dirs"), + + v.insert<string> ("c.std", variable_visibility::project), + + v.insert<strings> ("c.poptions"), + v.insert<strings> ("c.coptions"), + v.insert<strings> ("c.loptions"), + v.insert<strings> ("c.aoptions"), + v.insert<strings> ("c.libs"), + + nullptr /* c.importable_headers */, + + v["cc.poptions"], + v["cc.coptions"], + v["cc.loptions"], + v["cc.aoptions"], + v["cc.libs"], + + v.insert<strings> ("c.export.poptions"), + v.insert<strings> ("c.export.coptions"), + v.insert<strings> ("c.export.loptions"), + v.insert<vector<name>> ("c.export.libs"), + + v["cc.export.poptions"], + v["cc.export.coptions"], + v["cc.export.loptions"], + v["cc.export.libs"], + + v.insert_alias (v["cc.stdlib"], "c.stdlib"), // Same as cc.stdlib. + + v["cc.runtime"], + v["cc.stdlib"], + + v["cc.type"], + v["cc.system"], + v["cc.module_name"], + v["cc.reprocess"], + + v.insert<string> ("c.preprocessed"), // See cxx.preprocessed. + nullptr, // No __symexport (no modules). + + v.insert<string> ("c.id"), + v.insert<string> ("c.id.type"), + v.insert<string> ("c.id.variant"), + + v.insert<string> ("c.class"), + + v.insert<string> ("c.version"), + v.insert<uint64_t> ("c.version.major"), + v.insert<uint64_t> ("c.version.minor"), + v.insert<uint64_t> ("c.version.patch"), + v.insert<string> ("c.version.build"), + + v.insert<string> ("c.signature"), + v.insert<string> ("c.checksum"), + + v.insert<string> ("c.pattern"), + + v.insert<target_triplet> ("c.target"), + + v.insert<string> ("c.target.cpu"), + v.insert<string> ("c.target.vendor"), + v.insert<string> ("c.target.system"), + v.insert<string> ("c.target.version"), + v.insert<string> ("c.target.class") + }; + + // Alias some cc. variables as c. + // + v.insert_alias (d.c_runtime, "c.runtime"); + + assert (mod == nullptr); + config_module* m (new config_module (move (d))); + mod.reset (m); + m->guess (rs, loc, hints); + return true; + } + + bool + config_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr<module_base>&, + bool, + bool, + const variable_map& hints) + { + tracer trace ("c::config_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "c.config module must be loaded in project root"; + + // Load c.guess. + // + if (!cast_false<bool> (rs["c.guess.loaded"])) + load_module (rs, rs, "c.guess", loc, false, hints); + + config_module& cm (*rs.lookup_module<config_module> ("c.guess")); + cm.init (rs, loc, hints); + return true; + } + + static const target_type* const hdr[] = + { + &h::static_type, + nullptr + }; + + static const target_type* const inc[] = + { + &h::static_type, + &c::static_type, + nullptr + }; + + bool + init (scope& rs, + scope& bs, + const location& loc, + unique_ptr<module_base>& mod, + bool, + bool, + const variable_map& hints) + { + tracer trace ("c::init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "c module must be loaded in project root"; + + // Load c.config. + // + if (!cast_false<bool> (rs["c.config.loaded"])) + load_module (rs, rs, "c.config", loc, false, hints); + + config_module& cm (*rs.lookup_module<config_module> ("c.guess")); + + cc::data d { + cm, + + "c.compile", + "c.link", + "c.install", + "c.uninstall", + + cm.ci_->id.type, + cm.ci_->id.variant, + cm.ci_->class_, + cm.ci_->version.major, + cm.ci_->version.minor, + cast<process_path> (rs[cm.x_path]), + cast<target_triplet> (rs[cm.x_target]), + + cm.tstd, + + false, // No C modules yet. + false, // No __symexport support since no modules. + + cast<dir_paths> (rs[cm.x_sys_lib_dirs]), + cast<dir_paths> (rs[cm.x_sys_inc_dirs]), + + cm.sys_lib_dirs_extra, + cm.sys_inc_dirs_extra, + + c::static_type, + nullptr, // No C modules yet. + hdr, + inc + }; + + assert (mod == nullptr); + module* m; + mod.reset (m = new module (move (d))); + m->init (rs, loc, hints); + return true; + } + + static const module_functions mod_functions[] = + { + // NOTE: don't forget to also update the documentation in init.hxx if + // changing anything here. + + {"c.guess", nullptr, guess_init}, + {"c.config", nullptr, config_init}, + {"c", nullptr, init}, + {nullptr, nullptr, nullptr} + }; + + const module_functions* + build2_c_load () + { + return mod_functions; + } + } +} diff --git a/libbuild2/c/init.hxx b/libbuild2/c/init.hxx new file mode 100644 index 0000000..92d12a3 --- /dev/null +++ b/libbuild2/c/init.hxx @@ -0,0 +1,32 @@ +// file : libbuild2/c/init.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUILD2_C_INIT_HXX +#define LIBBUILD2_C_INIT_HXX + +#include <libbuild2/types.hxx> +#include <libbuild2/utility.hxx> + +#include <libbuild2/module.hxx> + +#include <libbuild2/c/export.hxx> + +namespace build2 +{ + namespace c + { + // Module `c` does not require bootstrapping. + // + // Submodules: + // + // `c.guess` -- registers and sets some variables. + // `c.config` -- loads c.guess and sets more variables. + // `c` -- loads c.config and registers target types and rules. + // + extern "C" LIBBUILD2_C_SYMEXPORT const module_functions* + build2_c_load (); + } +} + +#endif // LIBBUILD2_C_INIT_HXX diff --git a/libbuild2/c/target.hxx b/libbuild2/c/target.hxx new file mode 100644 index 0000000..b35beab --- /dev/null +++ b/libbuild2/c/target.hxx @@ -0,0 +1,22 @@ +// file : libbuild2/c/target.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUILD2_C_TARGET_HXX +#define LIBBUILD2_C_TARGET_HXX + +#include <libbuild2/types.hxx> +#include <libbuild2/utility.hxx> + +#include <libbuild2/cc/target.hxx> + +namespace build2 +{ + namespace c + { + using cc::h; + using cc::c; + } +} + +#endif // LIBBUILD2_C_TARGET_HXX |