summaryrefslogtreecommitdiff
path: root/rtemstoolkit/rld-cc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'rtemstoolkit/rld-cc.cpp')
-rw-r--r--rtemstoolkit/rld-cc.cpp608
1 files changed, 608 insertions, 0 deletions
diff --git a/rtemstoolkit/rld-cc.cpp b/rtemstoolkit/rld-cc.cpp
new file mode 100644
index 0000000..a2b1be4
--- /dev/null
+++ b/rtemstoolkit/rld-cc.cpp
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 2011-2014, Chris Johns <chrisj@rtems.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+
+#include <fstream>
+
+#include <rld.h>
+#include <rld-cc.h>
+#include <rld-process.h>
+#include <rld-rtems.h>
+
+namespace rld
+{
+ namespace cc
+ {
+ static std::string cc; //< The CC executable as absolute path.
+ static bool cc_set; //< True when the CC has been set.
+ static std::string cc_name = "gcc"; //< The CC name, ie gcc, clang.
+
+ static std::string ld; //< The LD executable as absolute path.
+ static bool ld_set; //< True when the LD has been set.
+ static std::string ld_name = "gcc"; //< The LD name, ie gcc, clang.
+
+ static std::string exec_prefix; //< The CC/LD executable prefix.
+
+ static std::string cppflags; //< The CPP flags.
+ static std::string cflags; //< The CC flags.
+ static std::string cxxflags; //< The CXX flags.
+ static std::string ldflags; //< The LD flags.
+
+ static std::string warning_cflags; //< The warning flags in cflags.
+ static std::string include_cflags; //< The include flags in cflags.
+ static std::string machine_cflags; //< The machine flags in cflags.
+ static std::string spec_cflags; //< The spec flags in cflags.
+
+ static std::string install_path; //< The CC reported install path.
+ static std::string programs_path; //< The CC reported programs path.
+ static std::string libraries_path; //< The CC reported libraries path.
+
+ /**
+ * The list of standard libraries.
+ */
+ #define RPS RLD_PATHSTR_SEPARATOR_STR
+ static const char* std_lib_c = "libgcc.a" RPS "libssp.a" RPS "libc.a";
+ static const char* std_lib_cplusplus = "libstdc++.a";
+
+ const std::string
+ strip_cflags (const std::string& flags)
+ {
+ std::string oflags;
+ rld::strings flags_;
+ rld::split (flags_, flags);
+
+ for (rld::strings::iterator si = flags_.begin ();
+ si != flags_.end ();
+ ++si)
+ {
+ if (!rld::starts_with ((*si), "-O") && !rld::starts_with ((*si), "-g"))
+ oflags += ' ' + *si;
+ }
+
+ return rld::trim (oflags);
+ }
+
+ const std::string
+ filter_flags (const std::string& flags,
+ const std::string& ,
+ const std::string& ,
+ flag_type type,
+ std::string& warnings,
+ std::string& includes,
+ std::string& machines,
+ std::string& specs)
+ {
+ /*
+ * Defintion of flags to be filtered.
+ */
+ enum flag_group
+ {
+ fg_warning,
+ fg_include,
+ fg_machine,
+ fg_specs
+ };
+ struct flag_def
+ {
+ flag_group group; ///< The group this flag belong to.
+ const char* opt; ///< Option start.
+ int count; ///< Number of arguments with the option.
+ bool path; ///< Is this a path ?
+ int out; ///< If the flag type is set drop the opt..
+ };
+ const flag_def flag_defs[] =
+ {
+ { fg_warning, "-W", 1, false, ft_cppflags | ft_cflags | ft_ldflags },
+ { fg_include, "-I", 2, true, 0 },
+ { fg_include, "-isystem", 2, true, 0 },
+ { fg_include, "-sysroot", 2, true, 0 },
+ { fg_machine, "-O", 1, false, 0 },
+ { fg_machine, "-m", 1, false, 0 },
+ { fg_machine, "-f", 1, false, 0 },
+ { fg_specs, "-q", 1, false, 0 },
+ { fg_specs, "-B", 2, true, 0 },
+ { fg_specs, "--specs", 2, false, 0 }
+ };
+ const int flag_def_size = sizeof (flag_defs) / sizeof (flag_def);
+
+ std::string oflags;
+ rld::strings flags_;
+
+ rld::split (flags_, strip_cflags (flags));
+
+ warnings.clear ();
+ includes.clear ();
+ machines.clear ();
+ specs.clear ();
+
+ for (rld::strings::iterator si = flags_.begin ();
+ si != flags_.end ();
+ ++si)
+ {
+ std::string opts;
+ std::string& opt = *(si);
+ bool in = true;
+
+ for (int fd = 0; fd < flag_def_size; ++fd)
+ {
+ if (rld::starts_with (opt, flag_defs[fd].opt))
+ {
+ int opt_count = flag_defs[fd].count;
+ if (opt_count > 1)
+ {
+ /*
+ * See if the flag is just the option. If is not take one less
+ * because the option's argument is joined to the option.
+ */
+ if (opt != flag_defs[fd].opt)
+ {
+ opt_count -= 1;
+ /*
+ * @todo Path processing here. Not sure what it is needed for.
+ */
+ }
+ }
+ opts += ' ' + opt;
+ while (opt_count > 1)
+ {
+ ++si;
+ /*
+ * @todo Path processing here. Not sure what it is needed for.
+ */
+ opts += ' ' + (*si);
+ --opt_count;
+ }
+ switch (flag_defs[fd].group)
+ {
+ case fg_warning:
+ warnings += ' ' + opts;
+ break;
+ case fg_include:
+ includes += ' ' + opts;
+ break;
+ case fg_machine:
+ machines += ' ' + opts;
+ break;
+ case fg_specs:
+ specs += ' ' + opts;
+ break;
+ default:
+ throw rld::error ("Invalid group", "flag group");
+ }
+ if ((flag_defs[fd].out & type) != 0)
+ in = false;
+ break;
+ }
+ }
+
+ if (in)
+ oflags += ' ' + opts;
+ }
+
+ rld::trim (warnings);
+ rld::trim (includes);
+ rld::trim (machines);
+ rld::trim (specs);
+
+ return rld::trim (oflags);
+ }
+
+ const std::string
+ filter_flags (const std::string& flags,
+ const std::string& arch,
+ const std::string& path,
+ flag_type type)
+ {
+ if (type != ft_cflags)
+ {
+ std::string warnings;
+ std::string includes;
+ std::string machines;
+ std::string specs;
+ return filter_flags (flags,
+ arch,
+ path,
+ type,
+ warnings,
+ includes,
+ machines,
+ specs);
+ }
+ else
+ {
+ return filter_flags (flags,
+ arch,
+ path,
+ type,
+ warning_cflags,
+ include_cflags,
+ machine_cflags,
+ spec_cflags);
+ }
+ }
+
+ void
+ set_cc (const std::string& cc_)
+ {
+ cc = cc_;
+ cc_set = true;
+ }
+
+ const std::string
+ get_cc ()
+ {
+ return cc;
+ }
+
+ bool
+ is_cc_set ()
+ {
+ return cc_set;
+ }
+
+ void
+ set_ld (const std::string& ld_)
+ {
+ ld = ld_;
+ ld_set = true;
+ }
+
+ const std::string
+ get_ld ()
+ {
+ return ld;
+ }
+
+ bool
+ is_ld_set ()
+ {
+ return ld_set;
+ }
+
+ void
+ set_exec_prefix (const std::string& exec_prefix_)
+ {
+ exec_prefix = exec_prefix_;
+ }
+
+ const std::string
+ get_exec_prefix ()
+ {
+ return exec_prefix;
+ }
+
+ bool is_exec_prefix_set ()
+ {
+ return !exec_prefix.empty ();
+ }
+
+ void
+ set_flags (const std::string& flags,
+ const std::string& arch,
+ const std::string& path,
+ flag_type type)
+ {
+ std::string* oflags;
+ switch (type)
+ {
+ case ft_cppflags:
+ oflags = &cppflags;
+ break;
+ case ft_cflags:
+ oflags = &cflags;
+ break;
+ case ft_cxxflags:
+ oflags = &cxxflags;
+ break;
+ case ft_ldflags:
+ oflags = &ldflags;
+ break;
+ default:
+ throw rld::error ("Invalid flag type", "CC set flags");
+ }
+ (*oflags) = filter_flags (flags, arch, path, type);
+ }
+
+ void
+ set_flags (const std::string& flags, flag_type type)
+ {
+ std::string arch;
+ std::string path;
+ set_flags (flags, arch, path, type);
+ }
+
+ void
+ append_flags (const std::string& flags,
+ const std::string& arch,
+ const std::string& path,
+ flag_type type)
+ {
+ std::string* oflags;
+ switch (type)
+ {
+ case ft_cppflags:
+ oflags = &cppflags;
+ break;
+ case ft_cflags:
+ oflags = &cflags;
+ break;
+ case ft_cxxflags:
+ oflags = &cxxflags;
+ break;
+ case ft_ldflags:
+ oflags = &ldflags;
+ break;
+ default:
+ throw rld::error ("Invalid flag type", "CC set flags");
+ }
+ if (oflags->empty ())
+ *oflags += filter_flags (flags, arch, path, type);
+ else
+ *oflags += ' ' + filter_flags (flags, arch, path, type);
+ }
+
+ void
+ append_flags (const std::string& flags, flag_type type)
+ {
+ std::string arch;
+ std::string path;
+ append_flags (flags, arch, path, type);
+ }
+
+ const std::string
+ get_flags (flag_type type)
+ {
+ std::string* flags;
+ switch (type)
+ {
+ case ft_cppflags:
+ flags = &cppflags;
+ break;
+ case ft_cflags:
+ flags = &cflags;
+ break;
+ case ft_cxxflags:
+ flags = &cxxflags;
+ break;
+ case ft_ldflags:
+ flags = &ldflags;
+ break;
+ default:
+ throw rld::error ("Invalid flag type", "CC get flags");
+ }
+ return *flags;
+ }
+
+ const std::string
+ get_flags (flag_group group)
+ {
+ std::string* flags;
+ switch (group)
+ {
+ case fg_warning_flags:
+ flags = &warning_cflags;
+ break;
+ case fg_include_flags:
+ flags = &include_cflags;
+ break;
+ case fg_machine_flags:
+ flags = &machine_cflags;
+ break;
+ case fg_spec_flags:
+ flags = &spec_cflags;
+ break;
+ default:
+ throw rld::error ("Invalid flag group", "CC get flags");
+ }
+ return *flags;
+ }
+
+ void
+ append_flags (flag_type type, rld::process::arg_container& args)
+ {
+ const std::string* flags = 0;
+ switch (type)
+ {
+ case ft_cppflags:
+ flags = &cppflags;
+ break;
+ case ft_cflags:
+ flags = &cflags;
+ break;
+ case ft_cxxflags:
+ flags = &cxxflags;
+ break;
+ case ft_ldflags:
+ flags = &ldflags;
+ break;
+ default:
+ throw rld::error ("Invalid flag type", "CC append flags");
+ }
+ if (!flags->empty ())
+ rld::process::args_append (args, *flags);
+ }
+
+ void
+ make_cc_command (rld::process::arg_container& args)
+ {
+ /*
+ * Use the absolute path to CC if provided.
+ */
+ if (is_cc_set ())
+ {
+ args.push_back (cc);
+ }
+ else
+ {
+ std::string cmd = cc_name;
+ if (!exec_prefix.empty ())
+ cmd = exec_prefix + "-rtems" + rld::rtems::version () + '-' + cmd;
+ args.push_back (cmd);
+ }
+ }
+
+ void
+ make_ld_command (rld::process::arg_container& args)
+ {
+ /*
+ * Use the absolute path to LD if provided.
+ */
+ if (is_ld_set ())
+ {
+ args.push_back (get_ld ());
+ }
+ else
+ {
+ std::string cmd = ld_name;
+ if (!exec_prefix.empty ())
+ cmd = exec_prefix + "-rtems" + rld::rtems::version () + '-' + cmd;
+ args.push_back (cmd);
+ }
+ }
+
+ static bool
+ match_and_trim (const char* prefix, std::string& line, std::string& result)
+ {
+ std::string::size_type pos = ::strlen (prefix);
+ if (line.substr (0, pos) == prefix)
+ {
+ if (line[pos] == '=')
+ ++pos;
+ result = line.substr (pos, line.size () - pos - 1);
+ return true;
+ }
+ return false;
+ }
+
+ static void
+ search_dirs ()
+ {
+ rld::process::arg_container args;
+
+ make_cc_command (args);
+ append_flags (ft_cppflags, args);
+ append_flags (ft_cflags, args);
+ args.push_back ("-print-search-dirs");
+
+ rld::process::tempfile out;
+ rld::process::tempfile err;
+ rld::process::status status;
+
+ status = rld::process::execute (cc_name, args, out.name (), err.name ());
+
+ if ((status.type == rld::process::status::normal) &&
+ (status.code == 0))
+ {
+ if (rld::verbose () >= RLD_VERBOSE_DETAILS)
+ out.output (cc_name, std::cout, true);
+ out.open ();
+ while (true)
+ {
+ std::string line;
+ out.read_line (line);
+ if (line.size () == 0)
+ break;
+ if (match_and_trim ("install: ", line, install_path))
+ continue;
+ if (match_and_trim ("programs: ", line, programs_path))
+ continue;
+ if (match_and_trim ("libraries: ", line, libraries_path))
+ continue;
+ }
+ out.close ();
+ if (rld::verbose () >= RLD_VERBOSE_DETAILS)
+ {
+ std::cout << "cc::install: " << install_path << std::endl
+ << "cc::programs: " << programs_path << std::endl
+ << "cc::libraries: " << libraries_path << std::endl;
+ }
+ }
+ else
+ {
+ err.output (cc_name, std::cout);
+ }
+ }
+
+ void
+ get_library_path (std::string& name, std::string& path)
+ {
+ rld::process::arg_container args;
+
+ make_cc_command (args);
+ append_flags (ft_cppflags, args);
+ append_flags (ft_cflags, args);
+ args.push_back ("-print-file-name=" + name);
+
+ rld::process::tempfile out;
+ rld::process::tempfile err;
+ rld::process::status status;
+
+ status = rld::process::execute (cc_name, args, out.name (), err.name ());
+
+ if ((status.type == rld::process::status::normal) &&
+ (status.code == 0))
+ {
+ if (rld::verbose () >= RLD_VERBOSE_DETAILS)
+ out.output ("cc", std::cout, true);
+ out.open ();
+ out.read (path);
+ out.close ();
+ if (rld::verbose () >= RLD_VERBOSE_DETAILS)
+ std::cout << "cc::libpath: " << name << " -> " << path << std::endl;
+ }
+ else
+ {
+ err.output ("cc", std::cout);
+ }
+ }
+
+ void
+ get_standard_libpaths (rld::path::paths& libpaths)
+ {
+ search_dirs ();
+ rld::split (libpaths, libraries_path, RLD_PATHSTR_SEPARATOR);
+ }
+
+ void
+ get_standard_libs (rld::path::paths& libs,
+ rld::path::paths& libpaths,
+ bool cplusplus)
+ {
+ strings libnames;
+
+ rld::split (libnames, std_lib_c, RLD_PATHSTR_SEPARATOR);
+ if (cplusplus)
+ rld::path::path_split (std_lib_cplusplus, libnames);
+
+ for (strings::iterator lni = libnames.begin ();
+ lni != libnames.end ();
+ ++lni)
+ {
+ if (rld::verbose () >= RLD_VERBOSE_INFO)
+ std::cout << "cc::stdlib: " << *lni << std::endl;
+
+ std::string path;
+
+ rld::path::find_file (path, *lni, libpaths);
+ if (path.empty ())
+ throw rld::error ("Library not found: " + *lni, "getting standard libs");
+
+ libs.push_back (path);
+ }
+ }
+ }
+}