]> git.tdb.fi Git - builder.git/blob - source/gnulinker.cpp
Make tools architecture-aware and restore cross-compilation functionality
[builder.git] / source / gnulinker.cpp
1 #include <stdexcept>
2 #include <vector>
3 #include <msp/fs/dir.h>
4 #include <msp/fs/utils.h>
5 #include <msp/strings/format.h>
6 #include "builder.h"
7 #include "component.h"
8 #include "executable.h"
9 #include "externaltask.h"
10 #include "gnucompiler.h"
11 #include "gnulinker.h"
12 #include "objectfile.h"
13 #include "sharedlibrary.h"
14 #include "sourcepackage.h"
15 #include "staticlibrary.h"
16
17 using namespace std;
18 using namespace Msp;
19
20 GnuLinker::GnuLinker(Builder &b, const Architecture &a):
21         Tool(b, a, "LINK")
22 {
23         input_suffixes.push_back(".o");
24         input_suffixes.push_back(".a");
25
26         if(architecture->is_native())
27         {
28                 system_path.push_back("/lib");
29                 system_path.push_back("/usr/lib");
30                 if(architecture->match_name("pc-32-linux"))
31                         system_path.push_back("/usr/lib/i386-linux-gnu");
32                 else if(architecture->match_name("pc-64-linux"))
33                         system_path.push_back("/usr/lib/x86_64-linux-gnu");
34         }
35         else
36                 system_path.push_back("/usr/"+architecture->get_cross_prefix()+"/lib");
37
38         default_linker = new Linker(*this, "CC");
39         cxx_linker = new Linker(*this, "CXX");
40 }
41
42 GnuLinker::~GnuLinker()
43 {
44         delete default_linker;
45         delete cxx_linker;
46 }
47
48 Target *GnuLinker::create_target(const list<Target *> &sources, const string &arg) const
49 {
50         if(sources.empty())
51                 throw invalid_argument("GnuLinker::create_target");
52         list<ObjectFile *> objs;
53         Linker *linker = default_linker;
54         for(list<Target *>::const_iterator i=sources.begin(); i!=sources.end(); ++i)
55         {
56                 if(ObjectFile *obj = dynamic_cast<ObjectFile *>(*i))
57                 {
58                         objs.push_back(obj);
59                         if(obj->get_tool()->get_tag()=="CXX")
60                                 linker = cxx_linker;
61                 }
62                 else
63                         throw invalid_argument("GnuLinker::create_target");
64         }
65
66         const Component &comp = objs.front()->get_component();
67         Binary *bin = 0;
68         if(arg=="shared")
69                 bin = new SharedLibrary(builder, comp, objs);
70         else
71                 bin = new Executable(builder, comp, objs);
72         bin->set_tool(*linker);
73         return bin;
74 }
75
76 Task *GnuLinker::run(const Target &) const
77 {
78         throw logic_error("GnuLinker should not be run directly");
79 }
80
81
82 GnuLinker::Linker::Linker(GnuLinker &p, const string &compiler_tag):
83         SubTool(p)
84 {
85         const Tool &compiler = builder.get_toolchain().get_tool(compiler_tag);
86         if(dynamic_cast<const GnuCompiler *>(&compiler))
87                 executable = compiler.get_executable();
88         else
89         {
90                 string command;
91                 if(compiler_tag=="CC")
92                         command = "gcc";
93                 else if(compiler_tag=="CXX")
94                         command = "g++";
95                 else
96                         throw invalid_argument("GnuLinker::Linker::Linker");
97                 if(architecture->is_cross())
98                         command = format("%s-%s", architecture->get_cross_prefix(), command);
99                 executable = builder.get_vfs().find_binary(command);
100         }
101 }
102
103 Target *GnuLinker::Linker::create_target(const list<Target *> &sources, const string &arg) const
104 {
105         return parent.create_target(sources, arg);
106 }
107
108 Task *GnuLinker::Linker::run(const Target &target) const
109 {
110         const Binary &bin = dynamic_cast<const Binary &>(target);
111
112         vector<string> argv;
113         argv.push_back(executable->get_path().str());
114
115         const Component &comp = *bin.get_component();
116
117         if(const SharedLibrary *shlib = dynamic_cast<const SharedLibrary *>(&bin))
118         {
119                 argv.push_back("-shared");
120                 argv.push_back("-fPIC");
121                 if(!shlib->get_soname().empty())
122                         argv.push_back("-Wl,-soname,"+shlib->get_soname());
123         }
124         else if(comp.get_package().get_library_mode()==ALL_STATIC)
125                 argv.push_back("-static");
126
127         const BuildInfo &binfo = comp.get_build_info();
128         for(BuildInfo::PathList::const_iterator i=binfo.libpath.begin(); i!=binfo.libpath.end(); ++i)
129                 argv.push_back("-L"+i->str());
130         if(binfo.strip)
131                 argv.push_back("-s");
132         if(binfo.threads)
133                 argv.push_back("-pthread");
134
135         const Architecture &native_arch = builder.get_native_arch();
136         if(architecture->get_bits()!=native_arch.get_bits())
137                 argv.push_back(format("-m%d", architecture->get_bits()));
138
139         FS::Path work_dir = comp.get_package().get_source();
140
141         argv.push_back("-o");
142         argv.push_back(relative(bin.get_path(), work_dir).str());
143
144         const Target::Dependencies &depends = target.get_depends();
145         for(Target::Dependencies::const_iterator i=depends.begin(); i!=depends.end(); ++i)
146         {
147                 Target *tgt = (*i)->get_real_target();
148
149                 if(ObjectFile *obj = dynamic_cast<ObjectFile *>(tgt))
150                         argv.push_back(relative(obj->get_path(), work_dir).str());
151                 else if(StaticLibrary *stlib = dynamic_cast<StaticLibrary *>(tgt))
152                         argv.push_back(stlib->get_path().str());
153                 else if(SharedLibrary *shlib = dynamic_cast<SharedLibrary *>(tgt))
154                         argv.push_back("-l"+shlib->get_libname());
155         }
156
157         if(!builder.get_dry_run())
158                 FS::mkpath(FS::dirname(bin.get_path()), 0755);
159
160         return new ExternalTask(argv, work_dir);
161 }