aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/buildfile2
-rw-r--r--build2/c/init.cxx13
-rw-r--r--build2/cc/common.cxx10
-rw-r--r--build2/cc/common.hxx16
-rw-r--r--build2/cc/compile.cxx518
-rw-r--r--build2/cc/guess.cxx18
-rw-r--r--build2/cc/guess.hxx29
-rw-r--r--build2/cc/link.cxx30
-rw-r--r--build2/cc/module.cxx23
-rw-r--r--build2/cc/pkgconfig.cxx4
-rw-r--r--build2/cxx/init.cxx22
11 files changed, 399 insertions, 286 deletions
diff --git a/build2/buildfile b/build2/buildfile
index ce75f07..9294738 100644
--- a/build2/buildfile
+++ b/build2/buildfile
@@ -28,7 +28,7 @@ else
else
stack_size = 4194304 # 4M
- if ($cxx.id == 'msvc')
+ if ($cxx.class == 'msvc')
cxx.loptions += "/STACK:$stack_size"
else
cxx.loptions += "-Wl,--stack,$stack_size"
diff --git a/build2/c/init.cxx b/build2/c/init.cxx
index e473e58..4158a09 100644
--- a/build2/c/init.cxx
+++ b/build2/c/init.cxx
@@ -21,6 +21,7 @@ namespace build2
namespace c
{
using cc::compiler_id;
+ using cc::compiler_class;
using cc::compiler_info;
class config_module: public cc::config_module
@@ -43,9 +44,9 @@ namespace build2
{
strings r;
- switch (ci.id.value ())
+ switch (ci.class_)
{
- case compiler_id::msvc:
+ 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
@@ -79,9 +80,7 @@ namespace build2
}
break;
}
- case compiler_id::gcc:
- case compiler_id::clang:
- case compiler_id::icc:
+ 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.
@@ -188,6 +187,8 @@ namespace build2
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"),
@@ -287,7 +288,7 @@ namespace build2
"c.uninstall",
cm.ci.id.value (),
- cm.ci.id.variant,
+ cm.ci.class_,
cm.ci.version.major,
cm.ci.version.minor,
cast<process_path> (rs[cm.x_path]),
diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx
index e022abb..951cc47 100644
--- a/build2/cc/common.cxx
+++ b/build2/cc/common.cxx
@@ -532,7 +532,7 @@ namespace build2
//
const char* e ("");
- if (cid == compiler_id::msvc)
+ if (cclass == compiler_class::msvc)
{
an = path (name);
e = "lib";
@@ -560,7 +560,7 @@ namespace build2
{
const char* e ("");
- if (cid == compiler_id::msvc)
+ if (cclass == compiler_class::msvc)
{
sn = path (name);
e = "dll.lib";
@@ -679,7 +679,7 @@ namespace build2
//
// If we didn't find .dll.lib then we cannot assume .lib is static.
//
- if (!an.empty () && (s != nullptr || cid != compiler_id::msvc))
+ if (!an.empty () && (s != nullptr || cclass != compiler_class::msvc))
{
f = d;
f /= an;
@@ -700,7 +700,7 @@ namespace build2
// Alternative search for VC.
//
- if (cid == compiler_id::msvc)
+ if (cclass == compiler_class::msvc)
{
const scope& rs (*p.scope->root_scope ());
const process_path& ld (cast<process_path> (rs["bin.ld.path"]));
@@ -899,7 +899,7 @@ namespace build2
dir_path d;
- if (cid == compiler_id::msvc)
+ if (cclass == compiler_class::msvc)
{
// /LIBPATH:<dir> (case-insensitive).
//
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index d4f8bd2..ba14471 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -78,6 +78,8 @@ namespace build2
const variable& x_id_type;
const variable& x_id_variant;
+ const variable& x_class;
+
const variable& x_version;
const variable& x_version_major;
const variable& x_version_minor;
@@ -105,13 +107,13 @@ namespace build2
// Cached values for some commonly-used variables/values.
//
- compiler_id::value_type cid; // x.id (no variant)
- const string& cvar; // x.id.variant
+ compiler_id::value_type cid; // x.id
+ compiler_class cclass; // x.class
uint64_t cmaj; // x.version.major
uint64_t cmin; // x.version.minor
const process_path& cpath; // x.path
- const target_triplet& ctg; // x.target
+ const target_triplet& ctgt; // x.target
const string& tsys; // x.target.system
const string& tclass; // x.target.class
@@ -158,10 +160,10 @@ namespace build2
const char* link,
const char* install,
const char* uninstall,
- compiler_id::value_type id, const string& var,
+ compiler_id::value_type id, compiler_class cl,
uint64_t mj, uint64_t mi,
const process_path& path,
- const target_triplet& tg,
+ const target_triplet& tgt,
const strings& std,
bool fm,
bool fs,
@@ -178,8 +180,8 @@ namespace build2
x_link (link),
x_install (install),
x_uninstall (uninstall),
- cid (id), cvar (var), cmaj (mj), cmin (mi), cpath (path),
- ctg (tg), tsys (ctg.system), tclass (ctg.class_),
+ cid (id), cclass (cl), cmaj (mj), cmin (mi), cpath (path),
+ ctgt (tgt), tsys (ctgt.system), tclass (ctgt.class_),
tstd (std),
modules (fm),
symexport (fs),
diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx
index 65c1362..fef7eb6 100644
--- a/build2/cc/compile.cxx
+++ b/build2/cc/compile.cxx
@@ -168,6 +168,7 @@ namespace build2
}
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
// Clang has *-cpp-output (but not c++-module-cpp-output) and they
// handle comments and line continuations. However, currently this
@@ -540,16 +541,21 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
e += mod ? "pcm" : o;
break;
}
case compiler_id::msvc:
- case compiler_id::icc:
{
e += mod ? "ifc" : o;
break;
}
+ case compiler_id::icc:
+ {
+ assert (!mod);
+ e += o;
+ }
}
// If we are compiling a module, then the obj*{} is an ad hoc member
@@ -1417,6 +1423,7 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
// -frewrite-includes is available since vanilla Clang 3.2.0.
//
@@ -1424,8 +1431,9 @@ namespace build2
// option (4.2 is based on 3.2svc so it may or may not have it and,
// no, we are not going to try to find out).
//
- if (cvar == "" ? (cmaj > 3 || (cmaj == 3 && cmin >= 2)) :
- cvar == "apple" ? (cmaj >= 5) : false)
+ if (cid == compiler_id::clang_apple
+ ? (cmaj >= 5)
+ : (cmaj > 3 || (cmaj == 3 && cmin >= 2)))
pp = "-frewrite-includes";
break;
@@ -1786,126 +1794,141 @@ namespace build2
// Don't treat warnings as errors.
//
- const char* werror (cid == compiler_id::msvc ? "/WX" : "-Werror");
+ const char* werror (nullptr);
+ switch (cclass)
+ {
+ case compiler_class::gcc: werror = "-Werror"; break;
+ case compiler_class::msvc: werror = "/WX"; break;
+ }
+
+ bool clang (cid == compiler_id::clang ||
+ cid == compiler_id::clang_apple);
append_options (args, t, c_coptions, werror);
append_options (args, t, x_coptions, werror);
append_options (args, tstd,
- tstd.size () -
- (modules && cid == compiler_id::clang ? 1 : 0));
+ tstd.size () - (modules && clang ? 1 : 0));
- if (cid == compiler_id::msvc)
+ switch (cclass)
{
- assert (pp != nullptr);
+ case compiler_class::msvc:
+ {
+ assert (pp != nullptr);
- args.push_back ("/nologo");
+ args.push_back ("/nologo");
- // See perform_update() for details on overriding the default
- // exceptions and runtime.
- //
- if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
- args.push_back ("/EHsc");
+ // See perform_update() for details on overriding the default
+ // exceptions and runtime.
+ //
+ if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
+ args.push_back ("/EHsc");
+
+ if (!find_option_prefixes ({"/MD", "/MT"}, args))
+ args.push_back ("/MD");
- if (!find_option_prefixes ({"/MD", "/MT"}, args))
- args.push_back ("/MD");
+ args.push_back ("/P"); // Preprocess to file.
+ args.push_back ("/showIncludes"); // Goes to stdout (with diag).
+ args.push_back (pp); // /C (preserve comments).
+ args.push_back ("/WX"); // Warning as error (see above).
- args.push_back ("/P"); // Preprocess to file.
- args.push_back ("/showIncludes"); // Goes to stdout (with diag).
- args.push_back (pp); // /C (preserve comments).
- args.push_back ("/WX"); // Warning as error (see above).
+ psrc = auto_rmfile (t.path () + x_pext);
- psrc = auto_rmfile (t.path () + x_pext);
+ if (cast<uint64_t> (rs[x_version_major]) >= 18)
+ {
+ args.push_back ("/Fi:");
+ args.push_back (psrc.path.string ().c_str ());
+ }
+ else
+ {
+ out = "/Fi" + psrc.path.string ();
+ args.push_back (out.c_str ());
+ }
- if (cast<uint64_t> (rs[x_version_major]) >= 18)
- {
- args.push_back ("/Fi:");
- args.push_back (psrc.path.string ().c_str ());
+ args.push_back (langopt (md)); // Compile as.
+ gen = args_gen = true;
+ break;
}
- else
+ case compiler_class::gcc:
{
- out = "/Fi" + psrc.path.string ();
- args.push_back (out.c_str ());
- }
+ if (t.is_a<objs> ())
+ {
+ // On Darwin, Win32 -fPIC is the default.
+ //
+ if (tclass == "linux" || tclass == "bsd")
+ args.push_back ("-fPIC");
+ }
- args.push_back (langopt (md)); // Compile as.
- gen = args_gen = true;
- }
- else
- {
- if (t.is_a<objs> ())
- {
- // On Darwin, Win32 -fPIC is the default.
+ // Depending on the compiler, decide whether (and how) we can
+ // produce preprocessed output as a side effect of dependency
+ // extraction.
//
- if (tclass == "linux" || tclass == "bsd")
- args.push_back ("-fPIC");
- }
+ // Note: -MM -MG skips missing <>-included.
- // Depending on the compiler, decide whether (and how) we can
- // produce preprocessed output as a side effect of dependency
- // extraction.
- //
- // Note: -MM -MG skips missing <>-included.
+ // Clang's -M does not imply -w (disable warnings). We also
+ // don't need them in the -MD case (see above) so disable for
+ // both.
+ //
+ if (clang)
+ args.push_back ("-w");
+
+ // Previously we used '*' as a target name but it gets expanded
+ // to the current directory file names by GCC (4.9) that comes
+ // with MSYS2 (2.4). Yes, this is the (bizarre) behavior of GCC
+ // being executed in the shell with -MQ '*' option and not just
+ // -MQ *.
+ //
+ args.push_back ("-MQ"); // Quoted target name.
+ args.push_back ("^"); // Old versions can't do empty target.
- // Clang's -M does not imply -w (disable warnings). We also don't
- // need them in the -MD case (see above) so disable for both.
- //
- if (cid == compiler_id::clang)
- args.push_back ("-w");
+ args.push_back ("-x");
+ args.push_back (langopt (md));
- // Previously we used '*' as a target name but it gets expanded to
- // the current directory file names by GCC (4.9) that comes with
- // MSYS2 (2.4). Yes, this is the (bizarre) behavior of GCC being
- // executed in the shell with -MQ '*' option and not just -MQ *.
- //
- args.push_back ("-MQ"); // Quoted target name.
- args.push_back ("^"); // Old versions can't do empty target name.
+ if (pp != nullptr)
+ {
+ // Note that the options are carefully laid out to be easy to
+ // override (see below).
+ //
+ args_i = args.size ();
- args.push_back ("-x");
- args.push_back (langopt (md));
+ args.push_back ("-MD");
+ args.push_back ("-E");
+ args.push_back (pp);
- if (pp != nullptr)
- {
- // Note that the options are carefully laid out to be easy to
- // override (see below).
- //
- args_i = args.size ();
+ // Dependency output.
+ //
+ args.push_back ("-MF");
- args.push_back ("-MD");
- args.push_back ("-E");
- args.push_back (pp);
+ // GCC is not capable of writing the dependency info to
+ // stdout. We also need to sense the diagnostics on the -E
+ // runs.
+ //
+ if (cid == compiler_id::gcc)
+ {
+ // Use the .t extension (for "temporary"; .d is taken).
+ //
+ r = &(drm = auto_rmfile (t.path () + ".t")).path;
+ args.push_back (r->string ().c_str ());
- // Dependency output.
- //
- args.push_back ("-MF");
+ sense_diag = true;
+ }
+ else
+ args.push_back ("-");
- // GCC is not capable of writing the dependency info to stdout.
- // We also need to sense the diagnostics on the -E runs.
- //
- if (cid == compiler_id::gcc)
- {
- // Use the .t extension (for "temporary"; .d is taken).
+ // Preprocessor output.
//
- r = &(drm = auto_rmfile (t.path () + ".t")).path;
- args.push_back (r->string ().c_str ());
-
- sense_diag = true;
+ psrc = auto_rmfile (t.path () + x_pext);
+ args.push_back ("-o");
+ args.push_back (psrc.path.string ().c_str ());
}
else
- args.push_back ("-");
+ {
+ args.push_back ("-M");
+ args.push_back ("-MG"); // Treat missing headers as generated.
+ }
- // Preprocessor output.
- //
- psrc = auto_rmfile (t.path () + x_pext);
- args.push_back ("-o");
- args.push_back (psrc.path.string ().c_str ());
- }
- else
- {
- args.push_back ("-M");
- args.push_back ("-MG"); // Treat missing headers as generated.
+ gen = args_gen = (pp == nullptr);
+ break;
}
-
- gen = args_gen = (pp == nullptr);
}
args.push_back (src.path ().string ().c_str ());
@@ -2376,13 +2399,14 @@ namespace build2
// For VC with /P the dependency info and diagnostics all go
// to stderr so redirect it to stdout.
//
- pr = process (cpath,
- args.data (),
- 0,
- -1,
- cid == compiler_id::msvc ? 1 : gen ? 2 : -2,
- nullptr, // CWD
- env.empty () ? nullptr : env.data ());
+ pr = process (
+ cpath,
+ args.data (),
+ 0,
+ -1,
+ cclass == compiler_class::msvc ? 1 : gen ? 2 : -2,
+ nullptr, // CWD
+ env.empty () ? nullptr : env.data ());
}
else
{
@@ -2441,134 +2465,145 @@ namespace build2
// Parse different dependency output formats.
//
- if (cid == compiler_id::msvc)
+ switch (cclass)
{
- if (first)
+ case compiler_class::msvc:
{
- // The first line should be the file we are compiling. If
- // it is not, then something went wrong even before we
- // could compile anything (e.g., file does not exist). In
- // this case the first line (and everything after it) is
- // presumably diagnostics.
- //
- if (l != src.path ().leaf ().string ())
+ if (first)
+ {
+ // The first line should be the file we are compiling.
+ // If it is not, then something went wrong even before
+ // we could compile anything (e.g., file does not
+ // exist). In this case the first line (and everything
+ // after it) is presumably diagnostics.
+ //
+ if (l != src.path ().leaf ().string ())
+ {
+ text << l;
+ bad_error = true;
+ break;
+ }
+
+ first = false;
+ continue;
+ }
+
+ string f (next_show (l, good_error));
+
+ if (f.empty ()) // Some other diagnostics.
{
text << l;
bad_error = true;
break;
}
- first = false;
- continue;
- }
+ // Skip until where we left off.
+ //
+ if (skip != 0)
+ {
+ // We can't be skipping over a non-existent header.
+ //
+ assert (!good_error);
+ skip--;
+ }
+ else
+ {
+ restart = add (path (move (f)), false, pmt);
+
+ // If the header does not exist (good_error), then
+ // restart must be true. Except that it is possible that
+ // someone running in parallel has already updated it.
+ // In this case we must force a restart since we haven't
+ // yet seen what's after this at-that-time-non-existent
+ // header.
+ //
+ // We also need to force the target update (normally
+ // done by add()).
+ //
+ if (good_error)
+ restart = updating = true;
- string f (next_show (l, good_error));
+ if (restart)
+ l6 ([&]{trace << "restarting";});
+ }
- if (f.empty ()) // Some other diagnostics.
- {
- text << l;
- bad_error = true;
break;
}
-
- // Skip until where we left off.
- //
- if (skip != 0)
- {
- // We can't be skipping over a non-existent header.
- //
- assert (!good_error);
- skip--;
- }
- else
+ case compiler_class::gcc:
{
- restart = add (path (move (f)), false, pmt);
-
- // If the header does not exist (good_error) then restart
- // must be true. Except that it is possible that someone
- // running in parallel has already updated it. In this
- // case we must force a restart since we haven't yet seen
- // what's after this at-that-time-non-existent header.
- //
- // We also need to force the target update (normally done
- // by add()).
+ // Make dependency declaration.
//
- if (good_error)
- restart = updating = true;
+ size_t pos (0);
- if (restart)
- l6 ([&]{trace << "restarting";});
- }
- }
- else
- {
- // Make dependency declaration.
- //
- size_t pos (0);
-
- if (first)
- {
- // Empty/invalid output should mean the wait() call below
- // will return false.
- //
- if (l.empty () ||
- l[0] != '^' || l[1] != ':' || l[2] != ' ')
+ if (first)
{
- if (!l.empty ())
- text << l;
-
- bad_error = true;
- break;
- }
+ // Empty/invalid output should mean the wait() call
+ // below will return false.
+ //
+ if (l.empty () ||
+ l[0] != '^' || l[1] != ':' || l[2] != ' ')
+ {
+ if (!l.empty ())
+ text << l;
- first = false;
- second = true;
+ bad_error = true;
+ break;
+ }
- // While normally we would have the source file on the
- // first line, if too long, it will be moved to the next
- // line and all we will have on this line is "^: \".
- //
- if (l.size () == 4 && l[3] == '\\')
- continue;
- else
- pos = 3; // Skip "^: ".
+ first = false;
+ second = true;
- // Fall through to the 'second' block.
- }
-
- if (second)
- {
- second = false;
- next_make (l, pos); // Skip the source file.
- }
+ // While normally we would have the source file on the
+ // first line, if too long, it will be moved to the next
+ // line and all we will have on this line is "^: \".
+ //
+ if (l.size () == 4 && l[3] == '\\')
+ continue;
+ else
+ pos = 3; // Skip "^: ".
- while (pos != l.size ())
- {
- string f (next_make (l, pos));
+ // Fall through to the 'second' block.
+ }
- // Skip until where we left off.
- //
- if (skip != 0)
+ if (second)
{
- skip--;
- continue;
+ second = false;
+ next_make (l, pos); // Skip the source file.
}
- restart = add (path (move (f)), false, pmt);
-
- if (restart)
+ while (pos != l.size ())
{
- l6 ([&]{trace << "restarting";});
- break;
+ string f (next_make (l, pos));
+
+ // Skip until where we left off.
+ //
+ if (skip != 0)
+ {
+ skip--;
+ continue;
+ }
+
+ restart = add (path (move (f)), false, pmt);
+
+ if (restart)
+ {
+ l6 ([&]{trace << "restarting";});
+ break;
+ }
}
+
+ break;
}
}
+
+ if (bad_error)
+ break;
}
// In case of VC, we are parsing stderr and if things go south,
// we need to copy the diagnostics for the user to see.
//
- if (bad_error && cid == compiler_id::msvc)
+ if (bad_error && cclass == compiler_class::msvc)
{
// We used to just dump the whole rdbuf but it turns out VC
// may continue writing include notes interleaved with the
@@ -2742,7 +2777,7 @@ namespace build2
// VC's preprocessed output, if present, is fully preprocessed.
//
- if (cid != compiler_id::msvc || !ps)
+ if (cclass != compiler_class::msvc || !ps)
{
// This should match with how we setup preprocessing and is pretty
// similar to init_args() from extract_headers().
@@ -2767,51 +2802,65 @@ namespace build2
//
// @@ Can be both -WX and /WX.
//
- const char* werror (cid == compiler_id::msvc ? "/WX" : "-Werror");
+ const char* werror (nullptr);
+ switch (cclass)
+ {
+ case compiler_class::gcc: werror = "-Werror"; break;
+ case compiler_class::msvc: werror = "/WX"; break;
+ }
+
+ bool clang (cid == compiler_id::clang ||
+ cid == compiler_id::clang_apple);
append_options (args, t, c_coptions, werror);
append_options (args, t, x_coptions, werror);
append_options (args, tstd,
- tstd.size () -
- (modules && cid == compiler_id::clang ? 1 : 0));
+ tstd.size () - (modules && clang ? 1 : 0));
- if (cid == compiler_id::msvc)
+ switch (cclass)
{
- args.push_back ("/nologo");
+ case compiler_class::msvc:
+ {
+ args.push_back ("/nologo");
- if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
- args.push_back ("/EHsc");
+ if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
+ args.push_back ("/EHsc");
- if (!find_option_prefixes ({"/MD", "/MT"}, args))
- args.push_back ("/MD");
+ if (!find_option_prefixes ({"/MD", "/MT"}, args))
+ args.push_back ("/MD");
- args.push_back ("/E");
- args.push_back ("/C");
- args.push_back (langopt (md)); // Compile as.
- }
- else
- {
- if (t.is_a<objs> ())
- {
- if (tclass == "linux" || tclass == "bsd")
- args.push_back ("-fPIC");
- }
-
- // Options that trigger preprocessing of partially preprocessed
- // output are a bit of a compiler-specific voodoo.
- //
- args.push_back ("-E");
+ args.push_back ("/E");
+ args.push_back ("/C");
+ args.push_back (langopt (md)); // Compile as.
- if (ps)
+ break;
+ }
+ case compiler_class::gcc:
{
- args.push_back ("-x");
- args.push_back (langopt (md));
+ if (t.is_a<objs> ())
+ {
+ if (tclass == "linux" || tclass == "bsd")
+ args.push_back ("-fPIC");
+ }
- if (cid == compiler_id::gcc)
+ // Options that trigger preprocessing of partially preprocessed
+ // output are a bit of a compiler-specific voodoo.
+ //
+ args.push_back ("-E");
+
+ if (ps)
{
- args.push_back ("-fpreprocessed");
- args.push_back ("-fdirectives-only");
+ args.push_back ("-x");
+ args.push_back (langopt (md));
+
+ if (cid == compiler_id::gcc)
+ {
+ args.push_back ("-fpreprocessed");
+ args.push_back ("-fdirectives-only");
+ }
}
+
+ break;
}
}
@@ -3034,6 +3083,7 @@ namespace build2
{
case compiler_id::gcc:
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
// We don't need to redo this if the above hash hasn't changed and
// the database is valid.
@@ -3791,6 +3841,7 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
// In Clang the module implementation's unit .pcm is special and
// must be "loaded".
@@ -3870,6 +3921,7 @@ namespace build2
switch (cid)
{
case compiler_id::gcc: break; // All of them.
+ case compiler_id::clang_apple:
case compiler_id::clang: n = ms.copied != 0 ? ms.copied : n; break;
case compiler_id::msvc: break; // All of them.
case compiler_id::icc: assert (false);
@@ -3898,6 +3950,7 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
// In Clang the module implementation's unit .pcm is special and
// must be "loaded".
@@ -4061,7 +4114,7 @@ namespace build2
strings mods; // Module options storage.
size_t out_i (0); // Index of the -o option.
- if (cid == compiler_id::msvc)
+ if (cclass == compiler_class::msvc)
{
// The /F*: option variants with separate names only became available
// in VS2013/12.0. Why do we bother? Because the command line suddenly
@@ -4194,6 +4247,7 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
args.push_back ("-o");
args.push_back (relm.string ().c_str ());
@@ -4242,6 +4296,7 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
// Clang handles comments and line continuations in the
// preprocessed source (it does not have -fpreprocessed).
@@ -4297,6 +4352,7 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
// Note that without -x Clang will treat .i/.ii as fully
// preprocessed.
@@ -4390,7 +4446,8 @@ namespace build2
// Clang's module compilation requires two separate compiler
// invocations.
//
- if (mod && cid == compiler_id::clang)
+ if (mod && (cid == compiler_id::clang ||
+ cid == compiler_id::clang_apple))
{
// Remove the target file if this fails. If we don't do that, we will
// end up with a broken build that is up-to-date.
@@ -4452,6 +4509,7 @@ namespace build2
switch (cid)
{
case id::gcc: return clean_extra (a, t, {".d", x_pext, ".t"});
+ case id::clang_apple:
case id::clang: return clean_extra (a, t, {".d", x_pext});
case id::msvc: return clean_extra (a, t, {".d", x_pext, ".idb", ".pdb"});
case id::icc: return clean_extra (a, t, {".d"});
diff --git a/build2/cc/guess.cxx b/build2/cc/guess.cxx
index da5be97..627283f 100644
--- a/build2/cc/guess.cxx
+++ b/build2/cc/guess.cxx
@@ -18,7 +18,7 @@ namespace build2
value () const -> value_type
{
if (type == "gcc") return gcc;
- if (type == "clang") return clang;
+ if (type == "clang") return variant.empty () ? clang : clang_apple;
if (type == "msvc") return msvc;
if (type == "icc") return icc;
@@ -26,6 +26,17 @@ namespace build2
return gcc;
}
+ string
+ to_string (compiler_class cl)
+ {
+ switch (cl)
+ {
+ case compiler_class::gcc: return "gcc";
+ case compiler_class::msvc: return "msvc";
+ }
+ return string (); // Never reached.
+ }
+
// Pre-guess the compiler type based on the compiler executable name.
// Return empty string if can't make a guess (for example, because the
// compiler name is a generic 'c++'). Note that it only guesses the type,
@@ -501,6 +512,7 @@ namespace build2
return compiler_info {
move (gr.path),
move (gr.id),
+ compiler_class::gcc,
move (v),
move (gr.signature),
move (gr.checksum), // Calculated on whole -v output.
@@ -617,6 +629,7 @@ namespace build2
return compiler_info {
move (gr.path),
move (gr.id),
+ compiler_class::gcc,
move (v),
move (gr.signature),
move (gr.checksum), // Calculated on whole -v output.
@@ -836,6 +849,7 @@ namespace build2
return compiler_info {
move (gr.path),
move (gr.id),
+ compiler_class::gcc, //@@ TODO: msvc on Windows?
move (v),
move (gr.signature),
cs.string (),
@@ -1059,6 +1073,7 @@ namespace build2
return compiler_info {
move (gr.path),
move (gr.id),
+ compiler_class::msvc,
move (v),
move (gr.signature),
cs.string (),
@@ -1107,6 +1122,7 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
assert (id.variant.empty () || id.variant == "apple");
r = guess_clang (xl, xc, c_coptions, x_coptions, move (gr));
diff --git a/build2/cc/guess.hxx b/build2/cc/guess.hxx
index 4d5d036..2eaa9d7 100644
--- a/build2/cc/guess.hxx
+++ b/build2/cc/guess.hxx
@@ -37,12 +37,11 @@ namespace build2
std::string
string () const {return variant.empty () ? type : type + "-" + variant;}
- // Note: does not include variant.
- //
enum value_type
{
gcc,
clang,
+ clang_apple,
msvc,
icc
};
@@ -57,6 +56,31 @@ namespace build2
return os << id.string ();
}
+ // Compiler class describes a set of compilers that follow more or less
+ // the same command line interface. Compilers that don't belong to any of
+ // the existing classes are in classes of their own (say, Sun CC would be
+ // on its own if we were to support it).
+ //
+ // Currently defined compiler classes:
+ //
+ // gcc gcc, clang, clang-apple, icc (on non-Windows)
+ // msvc msvc, clang-cl, icc (Windows)
+ //
+ enum class compiler_class
+ {
+ gcc,
+ msvc
+ };
+
+ string
+ to_string (compiler_class);
+
+ inline ostream&
+ operator<< (ostream& os, compiler_class cl)
+ {
+ return os << to_string (cl);
+ }
+
// Compiler version. Here we map the various compiler version formats to
// something that resembles the MAJOR.MINOR.PATCH-BUILD form of the
// Semantic Versioning. While the MAJOR.MINOR part is relatively
@@ -122,6 +146,7 @@ namespace build2
{
process_path path;
compiler_id id;
+ compiler_class class_;
compiler_version version;
string signature;
string checksum;
diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx
index 2b70fe9..24e81a6 100644
--- a/build2/cc/link.cxx
+++ b/build2/cc/link.cxx
@@ -220,7 +220,7 @@ namespace build2
// specified, then it should explicitly handle all the targets.
//
if (i == m->end ())
- fail << "no version for " << ctg << " in bin.lib.version" <<
+ fail << "no version for " << ctgt << " in bin.lib.version" <<
info << "considere adding " << tsys << "@<ver> or " << tclass
<< "@<ver>";
@@ -400,7 +400,7 @@ namespace build2
}
case otype::a:
{
- if (cid == compiler_id::msvc)
+ if (cclass == compiler_class::msvc)
e = "lib";
else
{
@@ -431,7 +431,7 @@ namespace build2
// Add VC's .pdb.
//
if (ot != otype::a &&
- cid == compiler_id::msvc &&
+ cclass == compiler_class::msvc &&
(find_option ("/DEBUG", t, c_loptions, true) ||
find_option ("/DEBUG", t, x_loptions, true)))
{
@@ -1363,7 +1363,7 @@ namespace build2
//
const string& cs (
cast<string> (
- rs[cid == compiler_id::msvc
+ rs[cclass == compiler_class::msvc
? var_pool["bin.ld.checksum"]
: x_checksum]));
@@ -1374,7 +1374,7 @@ namespace build2
// Next check the target. While it might be incorporated into the linker
// checksum, it also might not (e.g., VC link.exe).
//
- if (dd.expect (ctg.string ()) != nullptr)
+ if (dd.expect (ctgt.string ()) != nullptr)
l4 ([&]{trace << "target mismatch forcing update of " << t;});
// Start building the command line. While we don't yet know whether we
@@ -1394,7 +1394,7 @@ namespace build2
if (lt.static_library ())
{
- if (cid == compiler_id::msvc) ;
+ if (cclass == compiler_class::msvc) ;
else
{
// If the user asked for ranlib, don't try to do its function with
@@ -1406,7 +1406,7 @@ namespace build2
}
else
{
- if (cid == compiler_id::msvc)
+ if (cclass == compiler_class::msvc)
{
// We are using link.exe directly so don't pass the compiler
// options.
@@ -1428,7 +1428,7 @@ namespace build2
assert (sys_lib_dirs_extra <= sys_lib_dirs.size ());
append_option_values (
args,
- cid == compiler_id::msvc ? "/LIBPATH:" : "-L",
+ cclass == compiler_class::msvc ? "/LIBPATH:" : "-L",
sys_lib_dirs.begin () + sys_lib_dirs_extra, sys_lib_dirs.end (),
[] (const dir_path& d) {return d.string ().c_str ();});
@@ -1442,7 +1442,7 @@ namespace build2
auto l (t["bin.rpath"]);
if (l && !l->empty ())
- fail << ctg << " does not support rpath";
+ fail << ctgt << " does not support rpath";
}
else
{
@@ -1640,9 +1640,9 @@ namespace build2
{
ld = &cast<process_path> (rs["bin.ar.path"]);
- switch (cid)
+ switch (cclass)
{
- case compiler_id::msvc:
+ case compiler_class::msvc:
{
// lib.exe has /LIBPATH but it's not clear/documented what it's
// used for. Perhaps for link-time code generation (/LTCG)? If
@@ -1670,9 +1670,9 @@ namespace build2
// The options are usually similar enough to handle executables
// and shared libraries together.
//
- switch (cid)
+ switch (cclass)
{
- case compiler_id::msvc:
+ case compiler_class::msvc:
{
// Using link.exe directly.
//
@@ -1767,7 +1767,7 @@ namespace build2
args.push_back (out.c_str ());
break;
}
- default:
+ case compiler_class::gcc:
{
ld = &cpath;
@@ -1929,7 +1929,7 @@ namespace build2
// something like this) we are going to redirect stdout to stderr. For
// sane compilers this should be harmless.
//
- bool filter (cid == compiler_id::msvc && !lt.static_library ());
+ bool filter (cclass == compiler_class::msvc && !lt.static_library ());
process pr (*ld, args.data (), 0, (filter ? -1 : 2));
diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx
index badd0ef..92a04a7 100644
--- a/build2/cc/module.cxx
+++ b/build2/cc/module.cxx
@@ -122,6 +122,8 @@ namespace build2
rs.assign (x_id_type) = ci.id.type;
rs.assign (x_id_variant) = ci.id.variant;
+ rs.assign (x_class) = to_string (ci.class_);
+
rs.assign (x_version) = ci.version.string;
rs.assign (x_version_major) = ci.version.major;
rs.assign (x_version_minor) = ci.version.minor;
@@ -159,15 +161,20 @@ namespace build2
dir_paths lib_dirs;
dir_paths inc_dirs;
- if (ci.id.value () == compiler_id::msvc)
- {
- lib_dirs = msvc_library_search_paths (ci.path, rs);
- inc_dirs = msvc_header_search_paths (ci.path, rs);
- }
- else
+ switch (ci.class_)
{
- lib_dirs = gcc_library_search_paths (ci.path, rs);
- inc_dirs = gcc_header_search_paths (ci.path, rs);
+ case compiler_class::gcc:
+ {
+ lib_dirs = gcc_library_search_paths (ci.path, rs);
+ inc_dirs = gcc_header_search_paths (ci.path, rs);
+ break;
+ }
+ case compiler_class::msvc:
+ {
+ lib_dirs = msvc_library_search_paths (ci.path, rs);
+ inc_dirs = msvc_header_search_paths (ci.path, rs);
+ break;
+ }
}
sys_lib_dirs_extra = lib_dirs.size ();
diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx
index a9e3a34..13ba401 100644
--- a/build2/cc/pkgconfig.cxx
+++ b/build2/cc/pkgconfig.cxx
@@ -859,7 +859,7 @@ namespace build2
if (!lops.empty ())
{
- if (cid == compiler_id::msvc)
+ if (cclass == compiler_class::msvc)
{
// Translate -L to /LIBPATH.
//
@@ -1221,7 +1221,7 @@ namespace build2
//
n = l.path ().leaf ().base ().string ();
- if (cid != compiler_id::msvc)
+ if (cclass != compiler_class::msvc)
strip_lib ();
}
diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx
index 1ba452b..5b568fc 100644
--- a/build2/cxx/init.cxx
+++ b/build2/cxx/init.cxx
@@ -21,6 +21,7 @@ namespace build2
namespace cxx
{
using cc::compiler_id;
+ using cc::compiler_class;
using cc::compiler_info;
class config_module: public cc::config_module
@@ -43,7 +44,8 @@ namespace build2
{
strings r;
- auto id (ci.id.value ());
+ compiler_id::value_type id (ci.id.value ());
+ compiler_class cl (ci.class_);
uint64_t mj (ci.version.major);
uint64_t mi (ci.version.minor);
uint64_t p (ci.version.patch);
@@ -89,8 +91,9 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
- // Re-map Apple versions to vanilla Clang based on the following
+ // Remap Apple versions to vanilla Clang based on the following
// release point:
//
// 5.1 -> 3.4
@@ -99,7 +102,7 @@ namespace build2
// Note that this mapping is also used to enable experimental
// features below.
//
- if (ci.id.variant == "apple")
+ if (id == compiler_id::clang_apple)
{
if (mj >= 6) {mj = 3; mi = 5;}
else if (mj == 5 && mi >= 1) {mj = 3; mi = 4;}
@@ -170,6 +173,7 @@ namespace build2
break;
}
case compiler_id::clang:
+ case compiler_id::clang_apple:
{
// Enable starting with Clang 6.0.0.
//
@@ -196,9 +200,9 @@ namespace build2
{
// Otherwise translate the standard value.
//
- switch (id)
+ switch (cl)
{
- case compiler_id::msvc:
+ case compiler_class::msvc:
{
// C++ standard-wise, with VC you got what you got up until 14u2.
// Starting with 14u3 there is now the /std: switch which defaults
@@ -251,9 +255,7 @@ namespace build2
}
break;
}
- case compiler_id::gcc:
- case compiler_id::clang:
- case compiler_id::icc:
+ case compiler_class::gcc:
{
// Translate 11 to 0x, 14 to 1y, 17 to 1z, and 20 to 2a for
// compatibility with older versions of the compilers.
@@ -377,6 +379,8 @@ namespace build2
v.insert<string> ("cxx.id.type"),
v.insert<string> ("cxx.id.variant"),
+ v.insert<string> ("cxx.class"),
+
v.insert<string> ("cxx.version"),
v.insert<uint64_t> ("cxx.version.major"),
v.insert<uint64_t> ("cxx.version.minor"),
@@ -497,7 +501,7 @@ namespace build2
"cxx.uninstall",
cm.ci.id.value (),
- cm.ci.id.variant,
+ cm.ci.class_,
cm.ci.version.major,
cm.ci.version.minor,
cast<process_path> (rs[cm.x_path]),