/* * Copyright (c) 2011-2014, Chris Johns * * 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 #include #include #include #include #include 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); } } } }