From 49fd34cb82d8edae683526a5d9fdd3c86136e646 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 30 May 2017 10:23:27 +0200 Subject: Rework C/C++ standard translation in preparation for experimental/modules Also fix bug in clang-apple versioning. --- build2/c/init.cxx | 104 +++++++++++++----------- build2/cc/common.hxx | 20 +---- build2/cc/compile.cxx | 11 +-- build2/cc/gcc.cxx | 2 +- build2/cc/guess.cxx | 45 ++++++----- build2/cc/link.cxx | 2 +- build2/cc/module.cxx | 11 +-- build2/cc/module.hxx | 11 +-- build2/cxx/init.cxx | 217 +++++++++++++++++++++++++++++--------------------- 9 files changed, 232 insertions(+), 191 deletions(-) (limited to 'build2') diff --git a/build2/c/init.cxx b/build2/c/init.cxx index ada8596..998bc40 100644 --- a/build2/c/init.cxx +++ b/build2/c/init.cxx @@ -20,6 +20,7 @@ namespace build2 { namespace c { + using cc::compiler_id; using cc::compiler_info; class config_module: public cc::config_module @@ -29,64 +30,77 @@ namespace build2 config_module (config_data&& d) : config_data (move (d)), cc::config_module (move (d)) {} - string + strings translate_std (const compiler_info&, scope&, - const string&) const override; + const string*) const override; }; using cc::module; - string config_module:: - translate_std (const compiler_info& ci, scope& rs, const string& v) const + strings config_module:: + translate_std (const compiler_info& ci, scope& rs, const string* v) const { - string r; + strings r; - if (ci.id.type == "msvc") + switch (ci.id.value ()) { - // 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. - // - if (v != "90") + case compiler_id::msvc: { - uint64_t cver (ci.version.major); - - if ((v == "99" && cver < 16) || // Since VS2010/10.0. - (v == "11" && cver < 17)) // Since VS2012/11.0. + // 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. + // + if (v == nullptr) + ; + else if (*v != "90") { - fail << "C" << v << " is not supported by " << ci.signature << - info << "required by " << project (rs) << '@' << rs.out_path (); + uint64_t cver (ci.version.major); + + if ((*v == "99" && cver < 16) || // Since VS2010/10.0. + (*v == "11" && cver < 17)) // Since VS2012/11.0. + { + fail << "C" << *v << " is not supported by " << ci.signature << + info << "required by " << project (rs) << '@' << rs.out_path (); + } } + break; + } + case compiler_id::gcc: + case compiler_id::clang: + case compiler_id::icc: + { + // 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 o += *v; // In case the user specifies e.g., 'gnu11'. + + r.push_back (move (o)); + } + break; } - } - else - { - // 90 and 89 are the same standard. Translate 99 to 9x and 11 to 1x - // for compatibility with older versions of the compilers. - // - r = "-std="; - - if (v == "90") - r += "c90"; - else if (v == "99") - r += "c9x"; - else if (v == "11") - r += "c1x"; - else - r += v; // In case the user specifies something like 'gnu11'. } return r; diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx index 55e6675..fa6ec5a 100644 --- a/build2/cc/common.hxx +++ b/build2/cc/common.hxx @@ -112,7 +112,7 @@ namespace build2 const string& tsys; // x.target.system const string& tclass; // x.target.class - const string& tstd; // Translated x_std value (can be empty). + const strings& tstd; // Translated x_std value (options). const process_path* pkgconfig; // pkgconfig.path (can be NULL). const dir_paths& sys_lib_dirs; // x.sys_lib_dirs @@ -153,7 +153,7 @@ namespace build2 uint64_t mj, uint64_t mi, const process_path& path, const target_triplet& tg, - const string& std, + const strings& std, const process_path* pkgc, const dir_paths& sld, const dir_paths& sid, @@ -177,22 +177,6 @@ namespace build2 public: common (data&& d): data (move (d)) {} - // Language standard (x.std) mapping. - // - void - append_std (cstrings& args) const - { - if (!tstd.empty ()) - args.push_back (tstd.c_str ()); - } - - void - hash_std (sha256& cs) const - { - if (!tstd.empty ()) - cs.append (tstd); - } - // Library handling. // public: diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx index 5a95169..ce96e00 100644 --- a/build2/cc/compile.cxx +++ b/build2/cc/compile.cxx @@ -531,7 +531,7 @@ namespace build2 hash_options (cs, t, c_coptions); hash_options (cs, t, x_coptions); - hash_std (cs); + hash_options (cs, tstd); if (ct == otype::s) { @@ -1183,8 +1183,7 @@ namespace build2 // append_options (args, t, c_coptions); append_options (args, t, x_coptions); - - append_std (args); + append_options (args, tstd); if (cid == compiler_id::msvc) { @@ -2057,8 +2056,7 @@ namespace build2 // append_options (args, t, c_coptions); append_options (args, t, x_coptions); - - append_std (args); + append_options (args, tstd); if (cid == compiler_id::msvc) { @@ -2272,11 +2270,10 @@ namespace build2 append_options (args, t, c_coptions); append_options (args, t, x_coptions); + append_options (args, tstd); string out, out1; // Storage. - append_std (args); - if (cid == compiler_id::msvc) { // The /F*: option variants with separate names only became available diff --git a/build2/cc/gcc.cxx b/build2/cc/gcc.cxx index b5dd236..7f52a91 100644 --- a/build2/cc/gcc.cxx +++ b/build2/cc/gcc.cxx @@ -38,7 +38,7 @@ namespace build2 args.push_back (xc.recall_string ()); append_options (args, rs, c_coptions); append_options (args, rs, x_coptions); - if (!tstd.empty ()) args.push_back (tstd.c_str ()); + append_options (args, tstd); append_options (args, rs, c_loptions); append_options (args, rs, x_loptions); args.push_back ("-print-search-dirs"); diff --git a/build2/cc/guess.cxx b/build2/cc/guess.cxx index 9f14661..7dde013 100644 --- a/build2/cc/guess.cxx +++ b/build2/cc/guess.cxx @@ -1096,28 +1096,33 @@ namespace build2 compiler_info r; const compiler_id& id (gr.id); - if (id.type == "gcc") + switch (id.value ()) { - assert (id.variant.empty ()); - r = guess_gcc (xl, xc, c_coptions, x_coptions, move (gr)); - } - else if (id.type == "clang") - { - assert (id.variant.empty () || id.variant == "apple"); - r = guess_clang (xl, xc, c_coptions, x_coptions, move (gr)); - } - else if (id.type == "icc") - { - assert (id.variant.empty ()); - r = guess_icc (xl, xc, c_coptions, x_coptions, move (gr)); - } - else if (id.type == "msvc") - { - assert (id.variant.empty ()); - r = guess_msvc (xl, xc, c_coptions, x_coptions, move (gr)); + case compiler_id::gcc: + { + assert (id.variant.empty ()); + r = guess_gcc (xl, xc, c_coptions, x_coptions, move (gr)); + break; + } + case compiler_id::clang: + { + assert (id.variant.empty () || id.variant == "apple"); + r = guess_clang (xl, xc, c_coptions, x_coptions, move (gr)); + break; + } + case compiler_id::msvc: + { + assert (id.variant.empty ()); + r = guess_msvc (xl, xc, c_coptions, x_coptions, move (gr)); + break; + } + case compiler_id::icc: + { + assert (id.variant.empty ()); + r = guess_icc (xl, xc, c_coptions, x_coptions, move (gr)); + break; + } } - else - assert (false); // Derive binutils pattern unless this has already been done by the // compiler-specific code. diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index 91c2739..5c75d34 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -1192,7 +1192,7 @@ namespace build2 { append_options (args, t, c_coptions); append_options (args, t, x_coptions); - append_std (args); + append_options (args, tstd); } append_options (args, t, c_loptions); diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx index 885d6c7..cdc45e5 100644 --- a/build2/cc/module.cxx +++ b/build2/cc/module.cxx @@ -118,10 +118,9 @@ namespace build2 } } - // Translate x_std value (if any) to the compiler option (if any). + // Translate x_std value (if any) to the compiler option(s) (if any). // - if (auto l = rs[x_std]) - tstd = translate_std (ci, rs, cast (*l)); + tstd = translate_std (ci, rs, cast_null (rs[x_std])); // Extract system library search paths from the compiler and determine // additional system include search paths. @@ -129,7 +128,7 @@ namespace build2 dir_paths lib_dirs; dir_paths inc_dirs; - if (ci.id.type == "msvc") + if (ci.id.value () == compiler_id::msvc) lib_dirs = msvc_library_search_paths (ci.path, rs); else { @@ -188,7 +187,9 @@ namespace build2 if (!tstd.empty ()) { - dr << " std " << tstd << '\n'; + dr << " std "; // One less space. + for (const string& o: tstd) dr << ' ' << o; + dr << '\n'; } if (!ci.cc_pattern.empty ()) // bin_pattern printed by bin diff --git a/build2/cc/module.hxx b/build2/cc/module.hxx index eaa82eb..db17998 100644 --- a/build2/cc/module.hxx +++ b/build2/cc/module.hxx @@ -32,13 +32,14 @@ namespace build2 void init (scope&, const location&, const variable_map&); - // Translate the x.std value to the standard-selecting option if there - // is any. + // Translate the x.std value (if any) to the standard-selecting + // option(s) (if any). May also check/set x.features.* variables on the + // root scope. // - virtual string - translate_std (const compiler_info&, scope&, const string&) const = 0; + virtual strings + translate_std (const compiler_info&, scope&, const string*) const = 0; - string tstd; + strings tstd; compiler_id::value_type cid; diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx index 72a8fe5..a8706a3 100644 --- a/build2/cxx/init.cxx +++ b/build2/cxx/init.cxx @@ -20,6 +20,7 @@ namespace build2 { namespace cxx { + using cc::compiler_id; using cc::compiler_info; class config_module: public cc::config_module @@ -29,128 +30,166 @@ namespace build2 config_module (config_data&& d) : config_data (move (d)), cc::config_module (move (d)) {} - string + strings translate_std (const compiler_info&, scope&, - const string&) const override; + const string*) const override; }; using cc::module; - string config_module:: - translate_std (const compiler_info& ci, scope& rs, const string& v) const + strings config_module:: + translate_std (const compiler_info& ci, scope& rs, const string* v) const { - string r; + strings r; - const string& t (ci.id.type); + auto id (ci.id.value ()); uint64_t mj (ci.version.major); uint64_t mi (ci.version.minor); uint64_t p (ci.version.patch); // Translate "latest" to the compiler/version-appropriate option. // - if (v == "latest") + if (v != nullptr && *v == "latest") { - if (t == "msvc") - { - // VC14u3 and later has /std:c++latest. - // - if (mj > 19 || (mj == 19 && (mi > 0 || (mi == 0 && p >= 24215)))) - r = "/std:c++latest"; - } - else if (t == "gcc") - { - if (mj >= 5) r = "-std=c++1z"; // 17 - else if (mj == 4 && mi >= 8) r = "-std=c++1y"; // 14 - else if (mj == 4 && mi >= 4) r = "-std=c++0x"; // 11 - } - else if (t == "clang") - { - if (mj > 3 || (mj == 3 && mi >= 5)) r = "-std=c++1z"; // 17 - else if (mj == 3 && mi >= 4) r = "-std=c++1y"; // 14 - else /* ??? */ r = "-std=c++0x"; // 11 - } - else if (t == "icc") + const char* o (nullptr); + + switch (id) { - if (mj >= 17) r = "-std=c++1z"; // 17 - else if (mj > 15 || (mj == 15 && p >= 3)) r = "-std=c++1y"; // 14 - else /* ??? */ r = "-std=c++0x"; // 11 + case compiler_id::msvc: + { + // VC14u3 and later has /std:c++latest. + // + if (mj > 19 || (mj == 19 && (mi > 0 || (mi == 0 && p >= 24215)))) + o = "/std:c++latest"; + + break; + } + case compiler_id::gcc: + { + if (mj >= 5) o = "-std=c++1z"; // 17 + else if (mj == 4 && mi >= 8) o = "-std=c++1y"; // 14 + else if (mj == 4 && mi >= 4) o = "-std=c++0x"; // 11 + + break; + } + case compiler_id::clang: + { + // Re-map Apple versions to vanilla Clang based on the following + // release point: + // + // 5.1 -> 3.4 + // 6.0 -> 3.5 + // + if (ci.id.variant == "apple") + { + if (mj >= 6) {mj = 3; mi = 5;} + else if (mj == 5 && mi >= 1) {mj = 3; mi = 4;} + else {mj = 3; mi = 0;} + } + + if (mj > 3 || (mj == 3 && mi >= 5)) o = "-std=c++1z"; // 17 + else if (mj == 3 && mi >= 4) o = "-std=c++1y"; // 14 + else /* ??? */ o = "-std=c++0x"; // 11 + + break; + } + case compiler_id::icc: + { + if (mj >= 17) o = "-std=c++1z"; // 17 + else if (mj > 15 || (mj == 15 && p >= 3)) o = "-std=c++1y"; // 14 + else /* ??? */ o = "-std=c++0x"; // 11 + + break; + } } - else - assert (false); + + if (o != nullptr) + r.push_back (o); return r; } // Otherwise translate the standard value. // - if (t == "msvc") + switch (id) { - // C++ standard-wise, with VC you got what you got up until 14u2. - // Starting with 14u3 there is now the /std: switch which defaults - // to c++14 but can be set to c++latest. - // - // The question is also 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 11, 14, and 17? 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). - // - // For now we are not going to bother doing this for C++03. - // - if (v != "98" && v != "03") + case compiler_id::msvc: { - bool sup (false); - - if (v == "11") // C++11 since VS2010/10.0. - { - sup = mj >= 16; - } - else if (v == "14") // C++14 since VS2015/14.0. - { - sup = mj >= 19; - } - else if (v == "17") // C++17 since VS2015/14.0u2. + // C++ standard-wise, with VC you got what you got up until 14u2. + // Starting with 14u3 there is now the /std: switch which defaults + // to c++14 but can be set to c++latest. + // + // The question is also 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 11, 14, and 17? 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). + // + // For now we are not going to bother doing this for C++03. + // + if (v == nullptr) + ; + else if (*v != "98" && *v != "03") { - // Note: the VC15 compiler version is 19.10. + bool sup (false); + + if (*v == "11") // C++11 since VS2010/10.0. + { + sup = mj >= 16; + } + else if (*v == "14") // C++14 since VS2015/14.0. + { + sup = mj >= 19; + } + else if (*v == "17") // C++17 since VS2015/14.0u2. + { + // Note: the VC15 compiler version is 19.10. + // + sup = (mj > 19 || + (mj == 19 && (mi > 0 || (mi == 0 && p >= 23918)))); + } + + if (!sup) + fail << "C++" << *v << " is not supported by " << ci.signature << + info << "required by " << project (rs) << '@' << rs.out_path (); + + // VC14u3 and later has /std: // - sup = mj > 19 || (mj == 19 && (mi > 0 || (mi == 0 && p >= 23918))); + if (mj > 19 || (mj == 19 && (mi > 0 || (mi == 0 && p >= 24215)))) + { + if (*v == "17") + r.push_back ("/std:c++latest"); + } } - - if (!sup) - fail << "C++" << v << " is not supported by " << ci.signature << - info << "required by " << project (rs) << '@' << rs.out_path (); - - // VC14u3 and later has /std: + break; + } + case compiler_id::gcc: + case compiler_id::clang: + case compiler_id::icc: + { + // Translate 11 to 0x, 14 to 1y, and 17 to 1z for compatibility with + // older versions of the compilers. // - if (mj > 19 || (mj == 19 && (mi > 0 || (mi == 0 && p >= 24215)))) + if (v == nullptr) + ; + else { - if (v == "17") - r = "/std:c++latest"; + string o ("-std="); + + if (*v == "98") o += "c++98"; + else if (*v == "03") o += "c++03"; + else if (*v == "11") o += "c++0x"; + else if (*v == "14") o += "c++1y"; + else if (*v == "17") o += "c++1z"; + else o += *v; // In case the user specifies e.g., 'gnu++17'. + + r.push_back (move (o)); } + break; } } - else - { - // Translate 11 to 0x, 14 to 1y, and 17 to 1z for compatibility with - // older versions of the compilers. - // - r = "-std="; - - if (v == "98") - r += "c++98"; - else if (v == "03") - r += "c++03"; - else if (v == "11") - r += "c++0x"; - else if (v == "14") - r += "c++1y"; - else if (v == "17") - r += "c++1z"; - else - r += v; // In case the user specifies something like 'gnu++17'. - } return r; } -- cgit v1.1