source: rtems-tools/linkers/rld-cc.cpp @ b28e8b3

4.104.115
Last change on this file since b28e8b3 was b28e8b3, checked in by Chris Johns <chrisj@…>, on 09/07/14 at 04:31:18

Refactor the rld-rtems support to remove the globals.

  • Property mode set to 100644
File size: 15.2 KB
Line 
1/*
2 * Copyright (c) 2011-2014, Chris Johns <chrisj@rtems.org>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <string.h>
18
19#include <fstream>
20
21#include <rld.h>
22#include <rld-cc.h>
23#include <rld-process.h>
24#include <rld-rtems.h>
25
26namespace rld
27{
28  namespace cc
29  {
30    static std::string cc;              //< The CC executable as absolute path.
31    static bool        cc_set;          //< True when the CC has been set.
32    static std::string cc_name = "gcc"; //< The CC name, ie gcc, clang.
33    static std::string exec_prefix;     //< The CC executable prefix.
34
35    static std::string cppflags;        //< The CPP flags.
36    static std::string cflags;          //< The CC flags.
37    static std::string cxxflags;        //< The CXX flags.
38    static std::string ldflags;         //< The LD flags.
39
40    static std::string warning_cflags;  //< The warning flags in cflags.
41    static std::string include_cflags;  //< The include flags in cflags.
42    static std::string machine_cflags;  //< The machine flags in cflags.
43    static std::string spec_cflags;     //< The spec flags in cflags.
44
45    static std::string install_path;    //< The CC reported install path.
46    static std::string programs_path;   //< The CC reported programs path.
47    static std::string libraries_path;  //< The CC reported libraries path.
48
49    /**
50     * The list of standard libraries.
51     */
52    #define RPS RLD_PATHSTR_SEPARATOR_STR
53    static const char* std_lib_c         = "libgcc.a" RPS "libssp.a" RPS "libc.a";
54    static const char* std_lib_cplusplus = "libstdc++.a";
55
56    const std::string
57    strip_cflags (const std::string& flags)
58    {
59      std::string  oflags;
60      rld::strings flags_;
61      rld::split (flags_, flags);
62
63      for (rld::strings::iterator si = flags_.begin ();
64           si != flags_.end ();
65           ++si)
66      {
67        if (!rld::starts_with ((*si), "-O") && !rld::starts_with ((*si), "-g"))
68          oflags += ' ' + *si;
69      }
70
71      return rld::trim (oflags);
72    }
73
74    const std::string
75    filter_flags (const std::string& flags,
76                  const std::string& ,
77                  const std::string& ,
78                  flag_type          type,
79                  std::string&       warnings,
80                  std::string&       includes,
81                  std::string&       machines,
82                  std::string&       specs)
83    {
84      /*
85       * Defintion of flags to be filtered.
86       */
87      enum flag_group
88      {
89        fg_warning,
90        fg_include,
91        fg_machine,
92        fg_specs
93      };
94      struct flag_def
95      {
96        flag_group  group;  ///< The group this flag belong to.
97        const char* opt;    ///< Option start.
98        int         count;  ///< Number of arguments with the option.
99        bool        path;   ///< Is this a path ?
100        int         out;    ///< If the flag type is set drop the opt..
101      };
102      const flag_def flag_defs[] =
103        {
104          { fg_warning, "-W",       1, false, ft_cppflags | ft_cflags | ft_ldflags },
105          { fg_include, "-I",       2, true,  0 },
106          { fg_include, "-isystem", 2, true,  0 },
107          { fg_include, "-sysroot", 2, true,  0 },
108          { fg_machine, "-O",       1, false, 0 },
109          { fg_machine, "-m",       1, false, 0 },
110          { fg_machine, "-f",       1, false, 0 },
111          { fg_specs,   "-q",       1, false, 0 },
112          { fg_specs,   "-B",       2, true,  0 },
113          { fg_specs,   "--specs",  2, false, 0 }
114        };
115      const int flag_def_size = sizeof (flag_defs) / sizeof (flag_def);
116
117      std::string  oflags;
118      rld::strings flags_;
119
120      rld::split (flags_, strip_cflags (flags));
121
122      warnings.clear ();
123      includes.clear ();
124      machines.clear ();
125      specs.clear ();
126
127      for (rld::strings::iterator si = flags_.begin ();
128           si != flags_.end ();
129           ++si)
130      {
131        std::string  opts;
132        std::string& opt = *(si);
133        bool         in = true;
134
135        for (int fd = 0; fd < flag_def_size; ++fd)
136        {
137          if (rld::starts_with (opt, flag_defs[fd].opt))
138          {
139            int opt_count = flag_defs[fd].count;
140            if (opt_count > 1)
141            {
142              /*
143               * See if the flag is just the option. If is not take one less
144               * because the option's argument is joined to the option.
145               */
146              if (opt != flag_defs[fd].opt)
147              {
148                opt_count -= 1;
149                /*
150                 * @todo Path processing here. Not sure what it is needed for.
151                 */
152              }
153            }
154            opts += ' ' + opt;
155            while (opt_count > 1)
156            {
157              ++si;
158              /*
159               * @todo Path processing here. Not sure what it is needed for.
160               */
161              opts += ' ' + (*si);
162              --opt_count;
163            }
164            switch (flag_defs[fd].group)
165            {
166              case fg_warning:
167                warnings += ' ' + opts;
168                break;
169              case fg_include:
170                includes += ' ' + opts;
171                break;
172              case fg_machine:
173                machines += ' ' + opts;
174                break;
175              case fg_specs:
176                specs += ' ' + opts;
177                break;
178              default:
179                throw rld::error ("Invalid group", "flag group");
180            }
181            if ((flag_defs[fd].out & type) != 0)
182              in = false;
183            break;
184          }
185        }
186
187        if (in)
188          oflags += ' ' + opts;
189      }
190
191      rld::trim (warnings);
192      rld::trim (includes);
193      rld::trim (machines);
194      rld::trim (specs);
195
196      return rld::trim (oflags);
197    }
198
199    const std::string
200    filter_flags (const std::string& flags,
201                  const std::string& arch,
202                  const std::string& path,
203                  flag_type          type)
204    {
205      if (type != ft_cflags)
206      {
207        std::string warnings;
208        std::string includes;
209        std::string machines;
210        std::string specs;
211        return filter_flags (flags,
212                             arch,
213                             path,
214                             type,
215                             warnings,
216                             includes,
217                             machines,
218                             specs);
219      }
220      else
221      {
222        return filter_flags (flags,
223                             arch,
224                             path,
225                             type,
226                             warning_cflags,
227                             include_cflags,
228                             machine_cflags,
229                             spec_cflags);
230      }
231    }
232
233    void
234    set_cc (const std::string& cc_)
235    {
236      cc = cc_;
237      cc_set = true;
238    }
239
240    const std::string
241    get_cc ()
242    {
243      return cc;
244    }
245
246    bool
247    is_cc_set ()
248    {
249      return cc_set;
250    }
251
252    void
253    set_exec_prefix (const std::string& exec_prefix_)
254    {
255      exec_prefix = exec_prefix_;
256    }
257
258    const std::string
259    get_exec_prefix ()
260    {
261      return exec_prefix;
262    }
263
264    bool is_exec_prefix_set ()
265    {
266      return !exec_prefix.empty ();
267    }
268
269    void
270    set_flags (const std::string& flags,
271               const std::string& arch,
272               const std::string& path,
273               flag_type          type)
274    {
275      std::string* oflags;
276      switch (type)
277      {
278        case ft_cppflags:
279          oflags = &cppflags;
280          break;
281        case ft_cflags:
282          oflags = &cflags;
283          break;
284        case ft_cxxflags:
285          oflags = &cxxflags;
286          break;
287        case ft_ldflags:
288          oflags = &ldflags;
289          break;
290        default:
291          throw rld::error ("Invalid flag type", "CC set flags");
292      }
293      (*oflags) = filter_flags (flags, arch, path, type);
294    }
295
296    void
297    set_flags (const std::string& flags, flag_type type)
298    {
299      std::string arch;
300      std::string path;
301      set_flags (flags, arch, path, type);
302    }
303
304    void
305    append_flags (const std::string& flags,
306                  const std::string& arch,
307                  const std::string& path,
308                  flag_type          type)
309    {
310      std::string* oflags;
311      switch (type)
312      {
313        case ft_cppflags:
314          oflags = &cppflags;
315          break;
316        case ft_cflags:
317          oflags = &cflags;
318          break;
319        case ft_cxxflags:
320          oflags = &cxxflags;
321          break;
322        case ft_ldflags:
323          oflags = &ldflags;
324          break;
325        default:
326          throw rld::error ("Invalid flag type", "CC set flags");
327      }
328      if (oflags->empty ())
329        *oflags += filter_flags (flags, arch, path, type);
330      else
331        *oflags += ' ' + filter_flags (flags, arch, path, type);
332    }
333
334    void
335    append_flags (const std::string& flags, flag_type type)
336    {
337      std::string arch;
338      std::string path;
339      append_flags (flags, arch, path, type);
340    }
341
342    const std::string
343    get_flags (flag_type type)
344    {
345      std::string* flags;
346      switch (type)
347      {
348        case ft_cppflags:
349          flags = &cppflags;
350          break;
351        case ft_cflags:
352          flags = &cflags;
353          break;
354        case ft_cxxflags:
355          flags = &cxxflags;
356          break;
357        case ft_ldflags:
358          flags = &ldflags;
359          break;
360        default:
361          throw rld::error ("Invalid flag type", "CC get flags");
362      }
363      return *flags;
364    }
365
366    const std::string
367    get_flags (flag_group group)
368    {
369      std::string* flags;
370      switch (group)
371      {
372        case fg_warning_flags:
373          flags = &warning_cflags;
374          break;
375        case fg_include_flags:
376          flags = &include_cflags;
377          break;
378        case fg_machine_flags:
379          flags = &machine_cflags;
380          break;
381        case fg_spec_flags:
382          flags = &spec_cflags;
383          break;
384        default:
385          throw rld::error ("Invalid flag group", "CC get flags");
386      }
387      return *flags;
388    }
389
390    void
391    append_flags (flag_type type, rld::process::arg_container& args)
392    {
393      const std::string* flags = 0;
394      switch (type)
395      {
396        case ft_cppflags:
397          flags = &cppflags;
398          break;
399        case ft_cflags:
400          flags = &cflags;
401          break;
402        case ft_cxxflags:
403          flags = &cxxflags;
404          break;
405        case ft_ldflags:
406          flags = &ldflags;
407          break;
408        default:
409          throw rld::error ("Invalid flag type", "CC append flags");
410      }
411      if (!flags->empty ())
412        rld::process::args_append (args, *flags);
413    }
414
415    void
416    make_cc_command (rld::process::arg_container& args)
417    {
418      /*
419       * Use the absolute path to CC if provided.
420       */
421      if (!cc.empty ())
422        args.push_back (cc);
423      else
424      {
425        std::string cmd = cc_name;
426        if (!exec_prefix.empty ())
427          cmd = exec_prefix + "-rtems" + rld::rtems::version () + '-' + cmd;
428        args.push_back (cmd);
429      }
430    }
431
432    static bool
433    match_and_trim (const char* prefix, std::string& line, std::string& result)
434    {
435      std::string::size_type pos = ::strlen (prefix);
436      if (line.substr (0, pos) == prefix)
437      {
438        if (line[pos] == '=')
439          ++pos;
440        result = line.substr (pos, line.size () - pos - 1);
441        return true;
442      }
443      return false;
444    }
445
446    static void
447    search_dirs ()
448    {
449      rld::process::arg_container args;
450
451      make_cc_command (args);
452      append_flags (ft_cppflags, args);
453      append_flags (ft_cflags, args);
454      args.push_back ("-print-search-dirs");
455
456      rld::process::tempfile out;
457      rld::process::tempfile err;
458      rld::process::status   status;
459
460      status = rld::process::execute (cc_name, args, out.name (), err.name ());
461
462      if ((status.type == rld::process::status::normal) &&
463          (status.code == 0))
464      {
465        if (rld::verbose () >= RLD_VERBOSE_DETAILS)
466          out.output (cc_name, std::cout, true);
467        out.open ();
468        while (true)
469        {
470          std::string line;
471          out.read_line (line);
472          if (line.size () == 0)
473            break;
474          if (match_and_trim ("install: ", line, install_path))
475            continue;
476          if (match_and_trim ("programs: ", line, programs_path))
477            continue;
478          if (match_and_trim ("libraries: ", line, libraries_path))
479            continue;
480        }
481        out.close ();
482        if (rld::verbose () >= RLD_VERBOSE_DETAILS)
483        {
484          std::cout << "cc::install: " << install_path << std::endl
485                    << "cc::programs: " << programs_path << std::endl
486                    << "cc::libraries: " << libraries_path << std::endl;
487        }
488      }
489      else
490      {
491        err.output (cc_name, std::cout);
492      }
493    }
494
495    void
496    get_library_path (std::string& name, std::string& path)
497    {
498      rld::process::arg_container args;
499
500      make_cc_command (args);
501      append_flags (ft_cppflags, args);
502      append_flags (ft_cflags, args);
503      args.push_back ("-print-file-name=" + name);
504
505      rld::process::tempfile out;
506      rld::process::tempfile err;
507      rld::process::status   status;
508
509      status = rld::process::execute (cc_name, args, out.name (), err.name ());
510
511      if ((status.type == rld::process::status::normal) &&
512          (status.code == 0))
513      {
514        if (rld::verbose () >= RLD_VERBOSE_DETAILS)
515          out.output ("cc", std::cout, true);
516        out.open ();
517        out.read (path);
518        out.close ();
519        if (rld::verbose () >= RLD_VERBOSE_DETAILS)
520          std::cout << "cc::libpath: " << name << " -> " << path << std::endl;
521      }
522      else
523      {
524        err.output ("cc", std::cout);
525      }
526    }
527
528    void
529    get_standard_libpaths (rld::path::paths& libpaths)
530    {
531      search_dirs ();
532      rld::split (libpaths, libraries_path, RLD_PATHSTR_SEPARATOR);
533    }
534
535    void
536    get_standard_libs (rld::path::paths& libs,
537                       rld::path::paths& libpaths,
538                       bool              cplusplus)
539    {
540      strings libnames;
541
542      rld::split (libnames, std_lib_c, RLD_PATHSTR_SEPARATOR);
543      if (cplusplus)
544        rld::path::path_split (std_lib_cplusplus, libnames);
545
546      for (strings::iterator lni = libnames.begin ();
547           lni != libnames.end ();
548           ++lni)
549      {
550        if (rld::verbose () >= RLD_VERBOSE_INFO)
551          std::cout << "cc::stdlib: " << *lni << std::endl;
552
553        std::string path;
554
555        rld::path::find_file (path, *lni, libpaths);
556        if (path.empty ())
557          throw rld::error ("Library not found: " + *lni, "getting standard libs");
558
559        libs.push_back (path);
560      }
561    }
562  }
563}
Note: See TracBrowser for help on using the repository browser.