aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbuild2/c/init.cxx4
-rw-r--r--libbuild2/cc/common.hxx5
-rw-r--r--libbuild2/cc/compile-rule.cxx76
-rw-r--r--libbuild2/cc/compile-rule.hxx12
-rw-r--r--libbuild2/cc/gcc.cxx12
-rw-r--r--libbuild2/cc/guess.cxx127
-rw-r--r--libbuild2/cc/guess.hxx24
-rw-r--r--libbuild2/cc/init.cxx10
-rw-r--r--libbuild2/cc/link-rule.cxx20
-rw-r--r--libbuild2/cc/module.cxx284
-rw-r--r--libbuild2/cc/msvc.cxx6
-rw-r--r--libbuild2/cxx/init.cxx4
-rw-r--r--libbuild2/utility.cxx16
-rw-r--r--libbuild2/utility.hxx29
-rw-r--r--libbuild2/utility.ixx8
15 files changed, 402 insertions, 235 deletions
diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx
index bec20b5..893d285 100644
--- a/libbuild2/c/init.cxx
+++ b/libbuild2/c/init.cxx
@@ -159,7 +159,7 @@ namespace build2
// Note: some overridable, some not.
//
- v.insert<path> ("config.c", true),
+ v.insert<strings> ("config.c", true),
v.insert<string> ("config.c.id", true),
v.insert<string> ("config.c.version", true),
v.insert<string> ("config.c.target", true),
@@ -172,6 +172,7 @@ namespace build2
nullptr /* config.c.translatable_headers */,
v.insert<process_path> ("c.path"),
+ v.insert<strings> ("c.mode"),
v.insert<dir_paths> ("c.sys_lib_dirs"),
v.insert<dir_paths> ("c.sys_inc_dirs"),
@@ -335,6 +336,7 @@ namespace build2
cm.x_info->version.major,
cm.x_info->version.minor,
cast<process_path> (rs[cm.x_path]),
+ cast<strings> (rs[cm.x_mode]),
cast<target_triplet> (rs[cm.x_target]),
cm.tstd,
diff --git a/libbuild2/cc/common.hxx b/libbuild2/cc/common.hxx
index 68546f6..0548d6f 100644
--- a/libbuild2/cc/common.hxx
+++ b/libbuild2/cc/common.hxx
@@ -56,6 +56,7 @@ namespace build2
const variable* config_x_translatable_headers;
const variable& x_path; // Compiler process path.
+ const variable& x_mode; // Compiler mode options.
const variable& x_sys_lib_dirs; // System library search directories.
const variable& x_sys_inc_dirs; // System header search directories.
@@ -147,6 +148,7 @@ namespace build2
uint64_t cmaj; // x.version.major
uint64_t cmin; // x.version.minor
const process_path& cpath; // x.path
+ const strings& cmode; // x.mode (options)
const target_triplet& ctgt; // x.target
const string& tsys; // x.target.system
@@ -204,6 +206,7 @@ namespace build2
compiler_class cl,
uint64_t mj, uint64_t mi,
const process_path& path,
+ const strings& mode,
const target_triplet& tgt,
const strings& std,
bool fm,
@@ -223,7 +226,7 @@ namespace build2
x_uninstall (uninstall),
ctype (ct), cvariant (cv), cclass (cl),
cmaj (mj), cmin (mi),
- cpath (path),
+ cpath (path), cmode (mode),
ctgt (tgt), tsys (ctgt.system), tclass (ctgt.class_),
tstd (std),
modules (fm),
diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx
index c29769f..c0f876c 100644
--- a/libbuild2/cc/compile-rule.cxx
+++ b/libbuild2/cc/compile-rule.cxx
@@ -884,6 +884,8 @@ namespace build2
cs.append ("-fPIC");
}
+ append_options (cs, cmode);
+
if (dd.expect (cs.string ()) != nullptr)
l4 ([&]{trace << "options mismatch forcing update of " << t;});
}
@@ -2972,6 +2974,8 @@ namespace build2
args.push_back ("/nologo");
+ append_options (args, cmode);
+
// See perform_update() for details on overriding the default
// exceptions and runtime.
//
@@ -3018,13 +3022,16 @@ namespace build2
if (ctype == compiler_type::clang && tsys == "win32-msvc")
{
- if (!find_options ({"-nostdlib", "-nostartfiles"}, args))
+ initializer_list<const char*> os {"-nostdlib", "-nostartfiles"};
+ if (!find_options (os, cmode) && !find_options (os, args))
{
args.push_back ("-D_MT");
args.push_back ("-D_DLL");
}
}
+ append_options (args, cmode);
+
// Setup the dynamic module mapper if needed.
//
// Note that it's plausible in the future we will use it even if
@@ -4089,7 +4096,7 @@ namespace build2
append_options (args, tstd,
tstd.size () - (modules && clang ? 1 : 0));
- append_headers (env, args, header_args, a, t, md, dd);
+ append_header_options (env, args, header_args, a, t, md, dd);
switch (cclass)
{
@@ -4097,6 +4104,8 @@ namespace build2
{
args.push_back ("/nologo");
+ append_options (args, cmode);
+
if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
args.push_back ("/EHsc");
@@ -4122,13 +4131,16 @@ namespace build2
if (ctype == compiler_type::clang && tsys == "win32-msvc")
{
- if (!find_options ({"-nostdlib", "-nostartfiles"}, args))
+ initializer_list<const char*> os {"-nostdlib", "-nostartfiles"};
+ if (!find_options (os, cmode) && !find_options (os, args))
{
args.push_back ("-D_MT");
args.push_back ("-D_DLL");
}
}
+ append_options (args, cmode);
+
args.push_back ("-E");
append_lang_options (args, md);
@@ -4659,8 +4671,8 @@ namespace build2
// handle this: after match all our prerequisite BMIs will have their
// prerequisite BMIs known, recursively. The only bit that is missing is
// the re-export flag of some sorts. As well as deciding where to handle
- // it: here or in append_modules(). After some meditation it became
- // clear handling it here will be simpler: we need to weed out
+ // it: here or in append_module_options(). After some meditation it
+ // became clear handling it here will be simpler: we need to weed out
// duplicates for which we can re-use the imports vector. And we may
// also need to save this "flattened" list of modules in depdb.
//
@@ -4678,8 +4690,8 @@ namespace build2
// (with all the re-exported by us at the back), we will go over them
// and copy all of their re-exported bmi{}s (using the position we
// saved on step #1). The end result will be a recursively-explored
- // list of imported bmi{}s that append_modules() can simply convert
- // to the list of options.
+ // list of imported bmi{}s that append_module_options() can simply
+ // convert to the list of options.
//
// One issue with this approach is that these copied targets will be
// executed which means we need to adjust their dependent counts
@@ -5363,16 +5375,16 @@ namespace build2
//
// Note that this function is called for both full preprocessing and
// compilation proper and in the latter case it is followed by a call
- // to append_modules().
+ // to append_module_options().
//
void compile_rule::
- append_headers (environment&,
- cstrings& args,
- small_vector<string, 2>& stor,
- action,
- const file&,
- const match_data& md,
- const path& dd) const
+ append_header_options (environment&,
+ cstrings& args,
+ small_vector<string, 2>& stor,
+ action,
+ const file&,
+ const match_data& md,
+ const path& dd) const
{
switch (ctype)
{
@@ -5404,16 +5416,17 @@ namespace build2
// Append module-related options.
//
// Note that this function is only called for the compilation proper and
- // after a call to append_headers() (so watch out for duplicate options).
+ // after a call to append_header_options() (so watch out for duplicate
+ // options).
//
void compile_rule::
- append_modules (environment& env,
- cstrings& args,
- small_vector<string, 2>& stor,
- action a,
- const file& t,
- const match_data& md,
- const path& dd) const
+ append_module_options (environment& env,
+ cstrings& args,
+ small_vector<string, 2>& stor,
+ action a,
+ const file& t,
+ const match_data& md,
+ const path& dd) const
{
unit_type ut (md.type);
const module_positions& ms (md.modules);
@@ -5428,7 +5441,7 @@ namespace build2
//
// Note that it is also used to specify the output BMI file.
//
- if (md.headers == 0 && // Done in append_headers()?
+ if (md.headers == 0 && // In append_header_options()?
(ms.start != 0 ||
ut == unit_type::module_iface ||
ut == unit_type::module_header))
@@ -5686,6 +5699,8 @@ namespace build2
args.push_back ("/nologo");
+ append_options (args, cmode);
+
// While we want to keep the low-level build as "pure" as possible,
// the two misguided defaults, exceptions and runtime, just have to be
// fixed. Otherwise the default build is pretty much unusable. But we
@@ -5722,8 +5737,8 @@ namespace build2
msvc_sanitize_cl (args);
- append_headers (env, args, header_args, a, t, md, md.dd);
- append_modules (env, args, module_args, a, t, md, md.dd);
+ append_header_options (env, args, header_args, a, t, md, md.dd);
+ append_module_options (env, args, module_args, a, t, md, md.dd);
// The presence of /Zi or /ZI causes the compiler to write debug info
// to the .pdb file. By default it is a shared file called vcNN.pdb
@@ -5803,7 +5818,8 @@ namespace build2
// Clang's MSVC.cpp will not link the default runtime if either
// -nostdlib or -nostartfiles is specified. Let's do the same.
//
- if (!find_options ({"-nostdlib", "-nostartfiles"}, args))
+ initializer_list<const char*> os {"-nostdlib", "-nostartfiles"};
+ if (!find_options (os, cmode) && !find_options (os, args))
{
args.push_back ("-D_MT");
args.push_back ("-D_DLL");
@@ -5846,8 +5862,10 @@ namespace build2
}
}
- append_headers (env, args, header_args, a, t, md, md.dd);
- append_modules (env, args, module_args, a, t, md, md.dd);
+ append_options (args, cmode);
+
+ append_header_options (env, args, header_args, a, t, md, md.dd);
+ append_module_options (env, args, module_args, a, t, md, md.dd);
// Note: the order of the following options is relied upon below.
//
diff --git a/libbuild2/cc/compile-rule.hxx b/libbuild2/cc/compile-rule.hxx
index 4c74016..1b9d9cc 100644
--- a/libbuild2/cc/compile-rule.hxx
+++ b/libbuild2/cc/compile-rule.hxx
@@ -160,14 +160,14 @@ namespace build2
make_header_sidebuild (action, const scope&, linfo, const file&) const;
void
- append_headers (environment&, cstrings&, small_vector<string, 2>&,
- action, const file&,
- const match_data&, const path&) const;
+ append_header_options (environment&, cstrings&, small_vector<string, 2>&,
+ action, const file&,
+ const match_data&, const path&) const;
void
- append_modules (environment&, cstrings&, small_vector<string, 2>&,
- action, const file&,
- const match_data&, const path&) const;
+ append_module_options (environment&, cstrings&, small_vector<string, 2>&,
+ action, const file&,
+ const match_data&, const path&) const;
// Compiler-specific language selection option. Return the number of
// options (arguments, really) appended.
diff --git a/libbuild2/cc/gcc.cxx b/libbuild2/cc/gcc.cxx
index 9eb8925..cf0ccdc 100644
--- a/libbuild2/cc/gcc.cxx
+++ b/libbuild2/cc/gcc.cxx
@@ -31,13 +31,11 @@ namespace build2
{
dir_paths r;
- cstrings args;
- string std; // Storage.
-
- args.push_back (xc.recall_string ());
+ cstrings args {xc.recall_string ()};
append_options (args, rs, c_coptions);
append_options (args, rs, x_coptions);
append_options (args, tstd);
+ append_options (args, rs, x_mode);
// Compile as.
//
@@ -182,15 +180,13 @@ namespace build2
//
dir_paths r;
- cstrings args;
- string std; // Storage.
-
- args.push_back (xc.recall_string ());
+ cstrings args {xc.recall_string ()};
append_options (args, rs, c_coptions);
append_options (args, rs, x_coptions);
append_options (args, tstd);
append_options (args, rs, c_loptions);
append_options (args, rs, x_loptions);
+ append_options (args, rs, x_mode);
args.push_back ("-print-search-dirs");
args.push_back (nullptr);
diff --git a/libbuild2/cc/guess.cxx b/libbuild2/cc/guess.cxx
index 6ee13a0..f40bb88 100644
--- a/libbuild2/cc/guess.cxx
+++ b/libbuild2/cc/guess.cxx
@@ -155,6 +155,7 @@ namespace build2
static string
stdlib (lang xl,
const process_path& xp,
+ const strings& x_mo,
const strings* c_po, const strings* x_po,
const strings* c_co, const strings* x_co,
const char* src)
@@ -164,6 +165,7 @@ namespace build2
if (x_po != nullptr) append_options (args, *x_po);
if (c_co != nullptr) append_options (args, *c_co);
if (x_co != nullptr) append_options (args, *x_co);
+ append_options (args, x_mo);
args.push_back ("-x");
switch (xl)
{
@@ -784,6 +786,7 @@ namespace build2
guess (const char* xm,
lang,
const path& xc,
+ const strings& x_mo,
const optional<compiler_id>& xi,
pre_guess_result& pre,
sha256& cs)
@@ -945,6 +948,36 @@ namespace build2
run_search_fail (xc);
}
+ // Run the compiler with the specified option (-v, --version, etc; can
+ // also be NULL) calling the specified function on each trimmed output
+ // line (see build2::run() for details).
+ //
+ // Note that we suppress all the compiler errors because we may be
+ // trying an unsupported option (but still consider the exit code).
+ //
+ //
+ cstrings args {xp.recall_string ()};
+ append_options (args, x_mo);
+ args.push_back (nullptr); // Placeholder for the option.
+ args.push_back (nullptr);
+
+ auto run = [&cs, &xp, &args] (const char* o,
+ auto&& f,
+ const char* const* env = nullptr,
+ bool checksum = false) -> guess_result
+ {
+ args[args.size () - 2] = o;
+
+ return build2::run<guess_result> (
+ 3 /* verbosity */,
+ process_env (xp, env),
+ args.data (),
+ forward<decltype(f)> (f),
+ false /* error */,
+ false /* ignore_exit */,
+ checksum ? &cs : nullptr);
+ };
+
// Start with -v. This will cover gcc and clang (including clang-cl).
//
// While icc also writes what may seem like something we can use to
@@ -1056,11 +1089,8 @@ namespace build2
// One notable consequence of this is that if the locale changes
// (e.g., via LC_ALL), then the compiler signature will most likely
// change as well because of the translated text.
-
- // Suppress all the compiler errors because we may be trying an
- // unsupported option (but still consider the exit code).
//
- r = run<guess_result> (3, xp, "-v", f, false, false, &cs);
+ r = run ("-v", f, nullptr /* env */, true /* checksum */);
if (r.empty ())
{
@@ -1120,7 +1150,7 @@ namespace build2
return guess_result ();
};
- r = run<guess_result> (3, xp, "--version", f, false);
+ r = run ("--version", f);
if (r.empty ())
{
@@ -1171,9 +1201,11 @@ namespace build2
// going to unset these variables for our test (interestingly, only CL
// seem to cause the problem but let's unset both, for good measure).
//
+ // This is also the reason why we don't pass the mode options.
+ //
const char* env[] = {"CL=", "_CL_=", nullptr};
- r = run<guess_result> (3, process_env (xp, env), f, false);
+ r = build2::run<guess_result> (3, process_env (xp, env), f, false);
if (r.empty ())
{
@@ -1426,6 +1458,7 @@ namespace build2
const path& xc,
const string* xv,
const string* xt,
+ const strings&,
const strings*, const strings*,
const strings*, const strings*,
const strings*, const strings*,
@@ -1649,6 +1682,7 @@ namespace build2
const path& xc,
const string* xv,
const string* xt,
+ const strings& x_mo,
const strings* c_po, const strings* x_po,
const strings* c_co, const strings* x_co,
const strings*, const strings*,
@@ -1747,9 +1781,11 @@ namespace build2
if (xt == nullptr)
{
- cstrings args {xp.recall_string (), "-print-multiarch"};
+ cstrings args {xp.recall_string ()};
if (c_co != nullptr) append_options (args, *c_co);
if (x_co != nullptr) append_options (args, *x_co);
+ append_options (args, x_mo);
+ args.push_back ("-print-multiarch"); // Note: position relied upon.
args.push_back (nullptr);
// The output of both -print-multiarch and -dumpmachine is a single
@@ -1764,7 +1800,7 @@ namespace build2
l5 ([&]{trace << xc << " doesn's support -print-multiarch, "
<< "falling back to -dumpmachine";});
- args[1] = "-dumpmachine";
+ args[args.size () - 2] = "-dumpmachine";
t = run<string> (3, xp, args.data (), f, false);
}
@@ -1797,9 +1833,10 @@ namespace build2
// documentation says that you should usually specify -lgcc.
//
string rt ("libgcc");
- string csl (tt.system == "mingw32"
- ? "msvc"
- : stdlib (xl, xp, c_po, x_po, c_co, x_co, c_stdlib_src));
+ string csl (
+ tt.system == "mingw32"
+ ? "msvc"
+ : stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, c_stdlib_src));
string xsl;
switch (xl)
{
@@ -1813,7 +1850,7 @@ namespace build2
"#include <bits/c++config.h> \n"
"stdlib:=\"libstdc++\" \n";
- xsl = stdlib (xl, xp, c_po, x_po, c_co, x_co, src);
+ xsl = stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, src);
break;
}
}
@@ -1847,6 +1884,7 @@ namespace build2
static clang_msvc_info
guess_clang_msvc (lang xl,
const process_path& xp,
+ const strings& x_mo,
const strings* c_co, const strings* x_co,
bool cl)
{
@@ -1855,6 +1893,7 @@ namespace build2
cstrings args {xp.recall_string ()};
if (c_co != nullptr) append_options (args, *c_co);
if (x_co != nullptr) append_options (args, *x_co);
+ append_options (args, x_mo);
if (cl)
{
@@ -2060,6 +2099,7 @@ namespace build2
const path& xc,
const string* xv,
const string* xt,
+ const strings& x_mo,
const strings* c_po, const strings* x_po,
const strings* c_co, const strings* x_co,
const strings* c_lo, const strings* x_lo,
@@ -2222,9 +2262,10 @@ namespace build2
if (xt == nullptr)
{
cstrings args {xp.recall_string ()};
- args.push_back (cl ? "/clang:-dumpmachine" : "-dumpmachine");
if (c_co != nullptr) append_options (args, *c_co);
if (x_co != nullptr) append_options (args, *x_co);
+ append_options (args, x_mo);
+ args.push_back (cl ? "/clang:-dumpmachine" : "-dumpmachine");
args.push_back (nullptr);
// The output of -dumpmachine is a single line containing just the
@@ -2266,7 +2307,7 @@ namespace build2
// (plus a couple of other useful bits like the VC installation
// directory and Platform SDK).
//
- clang_msvc_info mi (guess_clang_msvc (xl, xp, c_co, x_co, cl));
+ clang_msvc_info mi (guess_clang_msvc (xl, xp, x_mo, c_co, x_co, cl));
// Keep the CPU and replace the rest.
//
@@ -2352,8 +2393,9 @@ namespace build2
};
const string* o;
- if ((o = find_rtlib (x_lo)) != nullptr ||
- (o = find_rtlib (c_lo)) != nullptr)
+ if ((o = find_rtlib (&x_mo)) != nullptr ||
+ (o = find_rtlib (x_lo)) != nullptr ||
+ (o = find_rtlib (c_lo)) != nullptr)
{
rt = string (*o, 8);
}
@@ -2363,9 +2405,10 @@ namespace build2
else /* Mac OS, etc. */ rt = "compiler-rt";
}
- string csl (tt.system == "win32-msvc" || tt.system == "mingw32"
- ? "msvc"
- : stdlib (xl, xp, c_po, x_po, c_co, x_co, c_stdlib_src));
+ string csl (
+ tt.system == "win32-msvc" || tt.system == "mingw32"
+ ? "msvc"
+ : stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, c_stdlib_src));
string xsl;
switch (xl)
@@ -2394,7 +2437,7 @@ namespace build2
xsl = tt.system == "win32-msvc"
? "msvcp"
- : stdlib (xl, xp, c_po, x_po, c_co, x_co, src);
+ : stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, src);
break;
}
}
@@ -2424,11 +2467,15 @@ namespace build2
const path& xc,
const string* xv,
const string* xt,
+ const strings& x_mo,
const strings* c_po, const strings* x_po,
const strings* c_co, const strings* x_co,
const strings*, const strings*,
guess_result&& gr, sha256&)
{
+ //@@ TODO: this should be reviewed/revised if/when we get access
+ // to more recent ICC versions.
+
const process_path& xp (gr.path);
// Extract the version. If the version has the fourth component, then
@@ -2470,6 +2517,8 @@ namespace build2
// The -V output is sent to STDERR.
//
+ // @@ TODO: running without the mode options.
+ //
s = run<string> (3, xp, "-V", f, false);
if (s.empty ())
@@ -2575,6 +2624,8 @@ namespace build2
// "Intel(R)" "64"
// "Intel(R)" "MIC" (-dumpmachine says: x86_64-k1om-linux)
//
+ // @@ TODO: why can't we combine it with the previous -V run?
+ //
string t, ot;
if (xt == nullptr)
@@ -2585,9 +2636,11 @@ namespace build2
dr << info << "use config." << xm << ".target to override";
});
- cstrings args {xp.recall_string (), "-V"};
+ cstrings args {xp.recall_string ()};
if (c_co != nullptr) append_options (args, *c_co);
if (x_co != nullptr) append_options (args, *x_co);
+ append_options (args, x_mo);
+ args.push_back ("-V");
args.push_back (nullptr);
// The -V output is sent to STDERR.
@@ -2637,6 +2690,8 @@ namespace build2
// in the future. So instead we are going to use -dumpmachine and
// substitute the CPU.
//
+ // @@ TODO: running without the mode options.
+ //
{
auto f = [] (string& l, bool) {return move (l);};
t = run<string> (3, xp, "-dumpmachine", f);
@@ -2677,9 +2732,10 @@ namespace build2
// Linux/GCC.
//
string rt (tt.system == "win32-msvc" ? "msvc" : "libgcc");
- string csl (tt.system == "win32-msvc"
- ? "msvc"
- : stdlib (xl, xp, c_po, x_po, c_co, x_co, c_stdlib_src));
+ string csl (
+ tt.system == "win32-msvc"
+ ? "msvc"
+ : stdlib (xl, xp, x_mo, c_po, x_po, c_co, x_co, c_stdlib_src));
string xsl;
switch (xl)
{
@@ -2722,6 +2778,7 @@ namespace build2
const string* xis,
const string* xv,
const string* xt,
+ const strings& x_mo,
const strings* c_po, const strings* x_po,
const strings* c_co, const strings* x_co,
const strings* c_lo, const strings* x_lo)
@@ -2734,6 +2791,7 @@ namespace build2
cs.append (static_cast<size_t> (xl));
cs.append (xc.string ());
if (xis != nullptr) cs.append (*xis);
+ append_options (cs, x_mo);
if (c_po != nullptr) append_options (cs, *c_po);
if (x_po != nullptr) append_options (cs, *x_po);
if (c_co != nullptr) append_options (cs, *c_co);
@@ -2773,7 +2831,7 @@ namespace build2
if (pre.type != invalid_compiler_type)
{
- gr = guess (xm, xl, xc, xi, pre, cs);
+ gr = guess (xm, xl, xc, x_mo, xi, pre, cs);
if (gr.empty ())
{
@@ -2789,7 +2847,7 @@ namespace build2
}
if (gr.empty ())
- gr = guess (xm, xl, xc, xi, pre, cs);
+ gr = guess (xm, xl, xc, x_mo, xi, pre, cs);
if (gr.empty ())
fail << "unable to guess " << xl << " compiler type of " << xc <<
@@ -2797,6 +2855,7 @@ namespace build2
compiler_info (*gf) (
const char*, lang, const path&, const string*, const string*,
+ const strings&,
const strings*, const strings*,
const strings*, const strings*,
const strings*, const strings*,
@@ -2815,7 +2874,7 @@ namespace build2
}
compiler_info r (gf (xm, xl, xc, xv, xt,
- c_po, x_po, c_co, x_co, c_lo, x_lo,
+ x_mo, c_po, x_po, c_co, x_co, c_lo, x_lo,
move (gr), cs));
// By default use the signature line to generate the checksum.
@@ -2893,8 +2952,11 @@ namespace build2
return (cache[key] = move (r));
}
- path
- guess_default (lang xl, const string& cid, const string& pat)
+ strings
+ guess_default (lang xl,
+ const string& cid,
+ const string& pat,
+ const strings& mode)
{
compiler_id id (cid);
const char* s (nullptr);
@@ -2937,7 +2999,12 @@ namespace build2
}
}
- return path (apply_pattern (s, pat));
+ strings r;
+ r.reserve (mode.size () + 1);
+ r.push_back (apply_pattern (s, pat));
+ r.insert (r.end (), mode.begin (), mode.end ());
+
+ return r;
}
}
}
diff --git a/libbuild2/cc/guess.hxx b/libbuild2/cc/guess.hxx
index d9172dc..d93aaf9 100644
--- a/libbuild2/cc/guess.hxx
+++ b/libbuild2/cc/guess.hxx
@@ -239,23 +239,27 @@ namespace build2
// that most of it will be the same, at least for C and C++.
//
const compiler_info&
- guess (const char* xm, // Module (for variable names in diagnostics).
- lang xl, // Language.
- const path& xc, // Compiler path.
- const string* xi, // Compiler id (optional).
- const string* xv, // Compiler version (optional).
- const string* xt, // Compiler target (optional).
+ guess (const char* xm, // Module (for var names in diagnostics).
+ lang xl, // Language.
+ const path& xc, // Compiler path.
+ const string* xi, // Compiler id (optional).
+ const string* xv, // Compiler version (optional).
+ const string* xt, // Compiler target (optional).
+ const strings& x_mode, // Compiler mode options.
const strings* c_poptions, const strings* x_poptions,
const strings* c_coptions, const strings* x_coptions,
const strings* c_loptions, const strings* x_loptions);
- // Given a language, compiler id, and optionally an (empty) pattern,
- // return an appropriate default compiler path.
+ // Given a language, compiler id, optional (empty) pattern, and mode
+ // return an appropriate default config.x value (compiler path and mode)
//
// For example, for (lang::cxx, gcc, *-4.9) we will get g++-4.9.
//
- path
- guess_default (lang, const string& cid, const string& pattern);
+ strings
+ guess_default (lang,
+ const string& cid,
+ const string& pattern,
+ const strings& mode);
}
}
diff --git a/libbuild2/cc/init.cxx b/libbuild2/cc/init.cxx
index f45a1bf..ac5823b 100644
--- a/libbuild2/cc/init.cxx
+++ b/libbuild2/cc/init.cxx
@@ -105,6 +105,7 @@ namespace build2
v.insert<string> ("config.cc.id");
v.insert<string> ("config.cc.hinter"); // Hinting module.
v.insert<string> ("config.cc.pattern");
+ v.insert<strings> ("config.cc.mode");
v.insert<target_triplet> ("config.cc.target");
// Compiler runtime and C standard library.
@@ -209,6 +210,15 @@ namespace build2
cast_empty<string> (h["config.cc.pattern"]);
}
+ // config.cc.mode
+ //
+ {
+ // This value could be hinted.
+ //
+ rs.assign<strings> ("cc.mode") =
+ cast_empty<strings> (h["config.cc.mode"]);
+ }
+
// cc.runtime
// cc.stdlib
//
diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx
index f10bd42..16a4ce7 100644
--- a/libbuild2/cc/link-rule.cxx
+++ b/libbuild2/cc/link-rule.cxx
@@ -2237,12 +2237,11 @@ namespace build2
}
else
{
- if (tsys == "win32-msvc")
- {
- // We are using link.exe directly so don't pass the compiler
- // options.
- }
- else
+ // Are we using the compiler or the linker (e.g., link.exe) directly?
+ //
+ bool ldc (tsys != "win32-msvc");
+
+ if (ldc)
{
append_options (args, t, c_coptions);
append_options (args, t, x_coptions);
@@ -2364,6 +2363,9 @@ namespace build2
sargs.push_back ("-Wl,-rpath-link," + p.string ());
}
}
+
+ if (ldc)
+ append_options (args, cmode);
}
// All the options should now be in. Hash them and compare with the db.
@@ -2560,8 +2562,10 @@ namespace build2
// See the runtime selection code in the compile rule for details
// on what's going on here.
//
- if (!find_options ({"-nostdlib", "-nostartfiles"}, t, c_coptions) &&
- !find_options ({"-nostdlib", "-nostartfiles"}, t, x_coptions))
+ initializer_list<const char*> os {"-nostdlib", "-nostartfiles"};
+ if (!find_options (os, cmode) &&
+ !find_options (os, t, c_coptions) &&
+ !find_options (os, t, x_coptions))
{
args.push_back ("/DEFAULTLIB:msvcrt");
args.push_back ("/DEFAULTLIB:oldnames");
diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx
index 641da5e..5f8652e 100644
--- a/libbuild2/cc/module.cxx
+++ b/libbuild2/cc/module.cxx
@@ -51,89 +51,114 @@ namespace build2
// config.x
//
-
- // Normally we will have a persistent configuration and computing the
- // default value every time will be a waste. So try without a default
- // first.
- //
- auto p (config::omitted (rs, config_x));
-
- if (!p.first)
+ strings mode;
{
- // If there is a config.x value for one of the modules that can hint
- // us the toolchain, load it's .guess module. This makes sure that the
- // order in which we load the modules is unimportant and that the user
- // can specify the toolchain using any of the config.x values.
+ // Normally we will have a persistent configuration and computing the
+ // default value every time will be a waste. So try without a default
+ // first.
//
- if (!cc_loaded)
+ auto p (config::omitted (rs, config_x));
+
+ if (!p.first)
{
- for (const char* const* pm (x_hinters); *pm != nullptr; ++pm)
+ // If there is a config.x value for one of the modules that can hint
+ // us the toolchain, load it's .guess module. This makes sure that
+ // the order in which we load the modules is unimportant and that
+ // the user can specify the toolchain using any of the config.x
+ // values.
+ //
+ if (!cc_loaded)
{
- string m (*pm);
+ for (const char* const* pm (x_hinters); *pm != nullptr; ++pm)
+ {
+ string m (*pm);
- // Must be the same as in module's init().
- //
- const variable& v (vp.insert<path> ("config." + m, true));
+ // Must be the same as in module's init().
+ //
+ const variable& v (vp.insert<strings> ("config." + m, true));
- if (rs[v].defined ())
- {
- load_module (rs, rs, m + ".guess", loc);
- cc_loaded = true;
- break;
+ if (rs[v].defined ())
+ {
+ load_module (rs, rs, m + ".guess", loc);
+ cc_loaded = true;
+ break;
+ }
}
}
+
+ // If cc.core.config is already loaded then use its toolchain id,
+ // (optional) pattern, and mode to guess an appropriate default
+ // (e.g., for {gcc, *-4.9 -m64} we will get g++-4.9 -m64).
+ //
+ strings d;
+
+ if (cc_loaded)
+ d = guess_default (x_lang,
+ cast<string> (rs["cc.id"]),
+ cast<string> (rs["cc.pattern"]),
+ cast<strings> (rs["cc.mode"]));
+ else
+ {
+ // Note that we don't have the default mode: it doesn't feel
+ // correct to default to, say, -m64 simply because that's how
+ // build2 was built.
+ //
+ d.push_back (x_default);
+
+ if (d.front ().empty ())
+ fail << "not built with default " << x_lang << " compiler" <<
+ info << "use " << config_x << " to specify";
+ }
+
+ // If this value was hinted, save it as commented out so that if the
+ // user changes the source of the pattern/mode, this one will get
+ // updated as well.
+ //
+ p = config::required (rs,
+ config_x,
+ move (d),
+ false,
+ cc_loaded ? config::save_commented : 0);
}
- // If cc.core.config is already loaded then use its toolchain id and
- // (optional) pattern to guess an appropriate default (e.g., for {gcc,
- // *-4.9} we will get g++-4.9).
+ // Split the value into the compiler path and mode.
//
- path d;
+ const strings& v (cast<strings> (*p.first));
- if (cc_loaded)
- d = guess_default (x_lang,
- cast<string> (rs["cc.id"]),
- cast<string> (rs["cc.pattern"]));
- else
- {
- d = path (x_default);
+ path xc;
+ try { xc = path (v.front ()); } catch (const invalid_path&) {}
- if (d.empty ())
- fail << "not built with default " << x_lang << " compiler" <<
- info << "use config." << x << " to specify";
- }
+ if (xc.empty ())
+ fail << "invalid path '" << v.front () << "' in " << config_x;
- // If this value was hinted, save it as commented out so that if the
- // user changes the source of the pattern, this one will get updated
- // as well.
+ mode.assign (++v.begin (), v.end ());
+
+ // Figure out which compiler we are dealing with, its target, etc.
+ //
+ // Note that we could allow guess() to modify mode to support
+ // imaginary options (such as /MACHINE for cl.exe). Though it's not
+ // clear what cc.mode would contain (original or modified).
//
- p = config::required (rs,
- config_x,
- d,
- false,
- cc_loaded ? config::save_commented : 0);
+ x_info = &build2::cc::guess (
+ x, x_lang, move (xc),
+ cast_null<string> (config::omitted (rs, config_x_id).first),
+ cast_null<string> (config::omitted (rs, config_x_version).first),
+ cast_null<string> (config::omitted (rs, config_x_target).first),
+ mode,
+ cast_null<strings> (rs[config_c_poptions]),
+ cast_null<strings> (rs[config_x_poptions]),
+ cast_null<strings> (rs[config_c_coptions]),
+ cast_null<strings> (rs[config_x_coptions]),
+ cast_null<strings> (rs[config_c_loptions]),
+ cast_null<strings> (rs[config_x_loptions]));
+
+ new_ = p.second;
}
- // Figure out which compiler we are dealing with, its target, etc.
- //
- x_info = &build2::cc::guess (
- x,
- x_lang,
- cast<path> (*p.first),
- cast_null<string> (config::omitted (rs, config_x_id).first),
- cast_null<string> (config::omitted (rs, config_x_version).first),
- cast_null<string> (config::omitted (rs, config_x_target).first),
- cast_null<strings> (rs[config_c_poptions]),
- cast_null<strings> (rs[config_x_poptions]),
- cast_null<strings> (rs[config_c_coptions]),
- cast_null<strings> (rs[config_x_coptions]),
- cast_null<strings> (rs[config_c_loptions]),
- cast_null<strings> (rs[config_x_loptions]));
-
const compiler_info& xi (*x_info);
- // Split/canonicalize the target. First see if the user asked us to
- // use config.sub.
+ // Split/canonicalize the target. First see if the user asked us to use
+ // config.sub.
//
target_triplet tt;
{
@@ -167,6 +192,9 @@ namespace build2
// Assign values to variables that describe the compiler.
//
+ rs.assign (x_path) = process_path (xi.path, false /* init */);
+ const strings& xm (cast<strings> (rs.assign (x_mode) = move (mode)));
+
rs.assign (x_id) = xi.id.string ();
rs.assign (x_id_type) = to_string (xi.id.type);
rs.assign (x_id_variant) = xi.id.variant;
@@ -187,6 +215,9 @@ namespace build2
assign_version (&x_variant_version,
xi.variant_version ? &*xi.variant_version : nullptr);
+ rs.assign (x_signature) = xi.signature;
+ rs.assign (x_checksum) = xi.checksum;
+
// Also enter as x.target.{cpu,vendor,system,version,class} for
// convenience of access.
//
@@ -203,8 +234,6 @@ namespace build2
if (!x_stdlib.alias (c_stdlib))
rs.assign (x_stdlib) = xi.x_stdlib;
- new_ = p.second;
-
// Load cc.core.guess.
//
if (!cc_loaded)
@@ -222,6 +251,9 @@ namespace build2
if (!xi.pattern.empty ())
h.assign ("config.cc.pattern") = xi.pattern;
+ if (!xm.empty ())
+ h.assign ("config.cc.mode") = xm;
+
h.assign (c_runtime) = xi.runtime;
h.assign (c_stdlib) = xi.c_stdlib;
@@ -262,6 +294,9 @@ namespace build2
// g++-7 vs gcc kind of mistakes. So now we warn since even if
// intentional, it is still probably a bad idea.
//
+ // Note also that it feels right to allow different modes (think
+ // -fexceptions for C or -fno-rtti for C++).
+ //
check (cast<string> (rs["cc.pattern"]),
cast<string> (rs[x_pattern]),
"toolchain pattern",
@@ -299,6 +334,37 @@ namespace build2
const compiler_info& xi (*x_info);
const target_triplet& tt (cast<target_triplet> (rs[x_target]));
+ // config.x.{p,c,l}options
+ // config.x.libs
+ //
+ // These are optional. We also merge them into the corresponding
+ // x.* variables.
+ //
+ // The merging part gets a bit tricky if this module has already
+ // been loaded in one of the outer scopes. By doing the straight
+ // append we would just be repeating the same options over and
+ // over. So what we are going to do is only append to a value if
+ // it came from this scope. Then the usage for merging becomes:
+ //
+ // x.coptions = <overridable options> # Note: '='.
+ // using x
+ // x.coptions += <overriding options> # Note: '+='.
+ //
+ rs.assign (x_poptions) += cast_null<strings> (
+ config::optional (rs, config_x_poptions));
+
+ rs.assign (x_coptions) += cast_null<strings> (
+ config::optional (rs, config_x_coptions));
+
+ rs.assign (x_loptions) += cast_null<strings> (
+ config::optional (rs, config_x_loptions));
+
+ rs.assign (x_aoptions) += cast_null<strings> (
+ config::optional (rs, config_x_aoptions));
+
+ rs.assign (x_libs) += cast_null<strings> (
+ config::optional (rs, config_x_libs));
+
// config.x.std overrides x.std
//
{
@@ -318,6 +384,22 @@ namespace build2
tstd = translate_std (xi, rs, v);
}
+ // config.x.translatable_header
+ //
+ // It's still fuzzy whether specifying (or maybe tweaking) this list in
+ // the configuration will be a common thing to do so for now we use
+ // omitted. It's also probably too early to think whether we should have
+ // the cc.* version and what the semantics should be.
+ //
+ if (x_translatable_headers != nullptr)
+ {
+ lookup l (config::omitted (rs, *config_x_translatable_headers).first);
+
+ // @@ MODHDR: if(modules) ?
+ //
+ rs.assign (x_translatable_headers) += cast_null<strings> (l);
+ }
+
// Extract system header/library search paths from the compiler and
// determine if we need any additional search paths.
//
@@ -443,12 +525,27 @@ namespace build2
//
if (verb >= (new_ ? 2 : 3))
{
+ const strings& mode (cast<strings> (rs[x_mode]));
+
diag_record dr (text);
{
dr << x << ' ' << project (rs) << '@' << rs << '\n'
- << " " << left << setw (11) << x << xi.path << '\n'
- << " id " << xi.id << '\n'
+ << " " << left << setw (11) << x << xi.path << '\n';
+ }
+
+ if (!mode.empty ())
+ {
+ dr << " mode "; // One space short.
+
+ for (const string& o: mode)
+ dr << ' ' << o;
+
+ dr << '\n';
+ }
+
+ {
+ dr << " id " << xi.id << '\n'
<< " version " << xi.version.string << '\n'
<< " major " << xi.version.major << '\n'
<< " minor " << xi.version.minor << '\n'
@@ -525,60 +622,9 @@ namespace build2
}
}
- rs.assign (x_path) = process_path (xi.path, false /* init */);
rs.assign (x_sys_lib_dirs) = move (lib_dirs);
rs.assign (x_sys_inc_dirs) = move (inc_dirs);
- rs.assign (x_signature) = xi.signature;
- rs.assign (x_checksum) = xi.checksum;
-
- // config.x.{p,c,l}options
- // config.x.libs
- //
- // These are optional. We also merge them into the corresponding
- // x.* variables.
- //
- // The merging part gets a bit tricky if this module has already
- // been loaded in one of the outer scopes. By doing the straight
- // append we would just be repeating the same options over and
- // over. So what we are going to do is only append to a value if
- // it came from this scope. Then the usage for merging becomes:
- //
- // x.coptions = <overridable options> # Note: '='.
- // using x
- // x.coptions += <overriding options> # Note: '+='.
- //
- rs.assign (x_poptions) += cast_null<strings> (
- config::optional (rs, config_x_poptions));
-
- rs.assign (x_coptions) += cast_null<strings> (
- config::optional (rs, config_x_coptions));
-
- rs.assign (x_loptions) += cast_null<strings> (
- config::optional (rs, config_x_loptions));
-
- rs.assign (x_aoptions) += cast_null<strings> (
- config::optional (rs, config_x_aoptions));
-
- rs.assign (x_libs) += cast_null<strings> (
- config::optional (rs, config_x_libs));
-
- // config.x.translatable_header
- //
- // It's still fuzzy whether specifying (or maybe tweaking) this list in
- // the configuration will be a common thing to do so for now we use
- // omitted. It's also probably too early to think whether we should have
- // the cc.* version and what the semantics should be.
- //
- if (x_translatable_headers != nullptr)
- {
- lookup l (config::omitted (rs, *config_x_translatable_headers).first);
-
- // @@ MODHDR: if(modules) ?
- //
- rs.assign (x_translatable_headers) += cast_null<strings> (l);
- }
-
// Load cc.core.config.
//
if (!cast_false<bool> (rs["cc.core.config.loaded"]))
diff --git a/libbuild2/cc/msvc.cxx b/libbuild2/cc/msvc.cxx
index e7251ac..a85e7a0 100644
--- a/libbuild2/cc/msvc.cxx
+++ b/libbuild2/cc/msvc.cxx
@@ -220,7 +220,8 @@ namespace build2
msvc_header_search_paths (const process_path&, scope&) const
{
// The compiler doesn't seem to have any built-in paths and all of them
- // come from the INCLUDE environment variable.
+ // either come from the INCLUDE environment variable or are specified
+ // explicitly on the command line.
// @@ VC: how are we going to do this? E.g., cl-14 does this internally.
// cl.exe /Be prints INCLUDE.
@@ -239,7 +240,8 @@ namespace build2
msvc_library_search_paths (const process_path&, scope&) const
{
// The linker doesn't seem to have any built-in paths and all of them
- // come from the LIB environment variable.
+ // either come from the LIB environment variable or are specified
+ // explicitly on the command line.
// @@ VC: how are we going to do this? E.g., cl-14 does this internally.
// cl.exe /Be prints LIB.
diff --git a/libbuild2/cxx/init.cxx b/libbuild2/cxx/init.cxx
index 9bf54eb..6237d99 100644
--- a/libbuild2/cxx/init.cxx
+++ b/libbuild2/cxx/init.cxx
@@ -391,7 +391,7 @@ namespace build2
// Note: some overridable, some not.
//
- v.insert<path> ("config.cxx", true),
+ v.insert<strings> ("config.cxx", true),
v.insert<string> ("config.cxx.id", true),
v.insert<string> ("config.cxx.version", true),
v.insert<string> ("config.cxx.target", true),
@@ -414,6 +414,7 @@ namespace build2
&v.insert<strings> ("config.cxx.translatable_headers", true),
v.insert<process_path> ("cxx.path"),
+ v.insert<strings> ("cxx.mode"),
v.insert<dir_paths> ("cxx.sys_lib_dirs"),
v.insert<dir_paths> ("cxx.sys_inc_dirs"),
@@ -612,6 +613,7 @@ namespace build2
cm.x_info->version.major,
cm.x_info->version.minor,
cast<process_path> (rs[cm.x_path]),
+ cast<strings> (rs[cm.x_mode]),
cast<target_triplet> (rs[cm.x_target]),
cm.tstd,
diff --git a/libbuild2/utility.cxx b/libbuild2/utility.cxx
index af4768c..78d2df2 100644
--- a/libbuild2/utility.cxx
+++ b/libbuild2/utility.cxx
@@ -386,13 +386,17 @@ namespace build2
}
bool
- find_options (initializer_list<const char*> os, const lookup& l, bool ic)
+ find_options (const initializer_list<const char*>& os,
+ const lookup& l,
+ bool ic)
{
return l && find_options (os, cast<strings> (l), ic);
}
bool
- find_options (initializer_list<const char*> os, const strings& strs, bool ic)
+ find_options (const initializer_list<const char*>& os,
+ const strings& strs,
+ bool ic)
{
for (const string& s: strs)
for (const char* o: os)
@@ -403,7 +407,7 @@ namespace build2
}
bool
- find_options (initializer_list<const char*> os,
+ find_options (const initializer_list<const char*>& os,
const cstrings& cstrs,
bool ic)
{
@@ -447,7 +451,7 @@ namespace build2
}
const string*
- find_option_prefixes (initializer_list<const char*> ps,
+ find_option_prefixes (const initializer_list<const char*>& ps,
const lookup& l,
bool ic)
{
@@ -455,7 +459,7 @@ namespace build2
}
const string*
- find_option_prefixes (initializer_list<const char*> ps,
+ find_option_prefixes (const initializer_list<const char*>& ps,
const strings& strs,
bool ic)
{
@@ -470,7 +474,7 @@ namespace build2
}
const char*
- find_option_prefixes (initializer_list<const char*> ps,
+ find_option_prefixes (const initializer_list<const char*>& ps,
const cstrings& cstrs,
bool ic)
{
diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx
index beacd2f..0422786 100644
--- a/libbuild2/utility.hxx
+++ b/libbuild2/utility.hxx
@@ -607,23 +607,32 @@ namespace build2
//
template <typename T>
bool
- find_options (initializer_list<const char*>,
+ find_options (const initializer_list<const char*>&,
T&,
const variable&,
bool = false);
template <typename T>
bool
- find_options (initializer_list<const char*>, T&, const char*, bool = false);
+ find_options (const initializer_list<const char*>&,
+ T&,
+ const char*,
+ bool = false);
LIBBUILD2_SYMEXPORT bool
- find_options (initializer_list<const char*>, const lookup&, bool = false);
+ find_options (const initializer_list<const char*>&,
+ const lookup&,
+ bool = false);
LIBBUILD2_SYMEXPORT bool
- find_options (initializer_list<const char*>, const strings&, bool = false);
+ find_options (const initializer_list<const char*>&,
+ const strings&,
+ bool = false);
LIBBUILD2_SYMEXPORT bool
- find_options (initializer_list<const char*>, const cstrings&, bool = false);
+ find_options (const initializer_list<const char*>&,
+ const cstrings&,
+ bool = false);
// As above but look for an option that has the specified prefix. Return the
// pointer to option or NULL if not found (thus can be used as bool).
@@ -651,29 +660,29 @@ namespace build2
//
template <typename T>
const string*
- find_option_prefixes (initializer_list<const char*>,
+ find_option_prefixes (const initializer_list<const char*>&,
T&,
const variable&,
bool = false);
template <typename T>
const string*
- find_option_prefixes (initializer_list<const char*>,
+ find_option_prefixes (const initializer_list<const char*>&,
T&,
const char*,
bool = false);
LIBBUILD2_SYMEXPORT const string*
- find_option_prefixes (initializer_list<const char*>,
+ find_option_prefixes (const initializer_list<const char*>&,
const lookup&, bool = false);
LIBBUILD2_SYMEXPORT const string*
- find_option_prefixes (initializer_list<const char*>,
+ find_option_prefixes (const initializer_list<const char*>&,
const strings&,
bool = false);
LIBBUILD2_SYMEXPORT const char*
- find_option_prefixes (initializer_list<const char*>,
+ find_option_prefixes (const initializer_list<const char*>&,
const cstrings&,
bool = false);
diff --git a/libbuild2/utility.ixx b/libbuild2/utility.ixx
index dcfd128..2846756 100644
--- a/libbuild2/utility.ixx
+++ b/libbuild2/utility.ixx
@@ -101,7 +101,7 @@ namespace build2
template <typename T>
inline bool
- find_options (initializer_list<const char*> os,
+ find_options (const initializer_list<const char*>& os,
T& s,
const variable& var,
bool ic)
@@ -111,7 +111,7 @@ namespace build2
template <typename T>
inline bool
- find_options (initializer_list<const char*> os,
+ find_options (const initializer_list<const char*>& os,
T& s,
const char* var,
bool ic)
@@ -135,7 +135,7 @@ namespace build2
template <typename T>
inline const string*
- find_option_prefixes (initializer_list<const char*> ps,
+ find_option_prefixes (const initializer_list<const char*>& ps,
T& s,
const variable& var,
bool ic)
@@ -145,7 +145,7 @@ namespace build2
template <typename T>
inline const string*
- find_option_prefixes (initializer_list<const char*> ps,
+ find_option_prefixes (const initializer_list<const char*>& ps,
T& s,
const char* var,
bool ic)