aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-08-24 22:10:09 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-08-28 15:01:48 +0300
commitf7adb1ce7a13753a6acf5c9eeb9577ecdada630c (patch)
tree5bd0bb1488c0a6fb35788ea815cbd7533a0de72a /libbuild2
parent4bdf53837e010073de802070d4e6087410662d3e (diff)
Move c build system module to separate library
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/buildfile2
-rw-r--r--libbuild2/c/buildfile90
-rw-r--r--libbuild2/c/export.hxx38
-rw-r--r--libbuild2/c/init.cxx375
-rw-r--r--libbuild2/c/init.hxx32
-rw-r--r--libbuild2/c/target.hxx22
-rw-r--r--libbuild2/module.cxx1
7 files changed, 559 insertions, 1 deletions
diff --git a/libbuild2/buildfile b/libbuild2/buildfile
index aad4e78..75731fc 100644
--- a/libbuild2/buildfile
+++ b/libbuild2/buildfile
@@ -5,7 +5,7 @@
# NOTE: remember to update bundled_modules in libbuild2/modules.cxx if adding
# a new module.
#
-./: lib{build2} bash/ bin/ cc/ in/ version/
+./: lib{build2} bash/ bin/ c/ cc/ in/ version/
import int_libs = libbutl%lib{butl}
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
diff --git a/libbuild2/module.cxx b/libbuild2/module.cxx
index 4e7080a..37bc817 100644
--- a/libbuild2/module.cxx
+++ b/libbuild2/module.cxx
@@ -39,6 +39,7 @@ namespace build2
static const char* bundled_modules[] = {
"bash",
"bin",
+ "c",
"cc",
"in",
"version"