1 #include "Builders.h"
2 #include "BuilderUtils.h"
3
AddFlags(Index<String> & cfg)4 void GccBuilder::AddFlags(Index<String>& cfg)
5 {
6 }
7
CompilerName() const8 String GccBuilder::CompilerName() const
9 {
10 if(!IsNull(compiler)) return compiler;
11 return "c++";
12 }
13
CmdLine(const String & package,const Package & pkg)14 String GccBuilder::CmdLine(const String& package, const Package& pkg)
15 {
16 String cc = CompilerName();
17 cc << " -c";
18 for(String s : pkg_config)
19 cc << " `pkg-config --cflags " << s << "`";
20 cc << ' ' << IncludesDefinesTargetTime(package, pkg);
21 return cc;
22 }
23
BinaryToObject(String objfile,CParser & binscript,String basedir,const String & package,const Package & pkg)24 void GccBuilder::BinaryToObject(String objfile, CParser& binscript, String basedir,
25 const String& package, const Package& pkg)
26 {
27 String fo = BrcToC(binscript, basedir);
28 String tmpfile = ForceExt(objfile, ".c");
29 SaveFile(tmpfile, fo);
30 String cc = CmdLine(package, pkg);
31 cc << " -c -o " << GetHostPathQ(objfile) << " -x c " << GetHostPathQ(tmpfile);
32 int slot = AllocSlot();
33 if(slot < 0 || !Run(cc, slot, objfile, 1))
34 throw Exc(Format("Error compiling binary object '%s'.", objfile));
35 }
36
BuildPackage(const String & package,Vector<String> & linkfile,Vector<String> & immfile,String & linkoptions,const Vector<String> & all_uses,const Vector<String> & all_libraries,int opt)37 bool GccBuilder::BuildPackage(const String& package, Vector<String>& linkfile, Vector<String>& immfile,
38 String& linkoptions, const Vector<String>& all_uses, const Vector<String>& all_libraries,
39 int opt)
40 {
41 if(HasFlag("OSX") && HasFlag("GUI")) {
42 String folder;
43 String name = GetFileName(target);
44 if(GetFileExt(target) == ".app")
45 target = target + "/Contents/MacOS/" + GetFileTitle(target);
46 else
47 target = target + ".app/Contents/MacOS/" + GetFileName(target);
48 RealizePath(target);
49 }
50
51 SaveBuildInfo(package);
52
53 int i;
54 String packagepath = PackagePath(package);
55 Package pkg;
56 pkg.Load(packagepath);
57 String packagedir = GetFileFolder(packagepath);
58 ChDir(packagedir);
59 PutVerbose("cd " + packagedir);
60 IdeConsoleBeginGroup(package);
61 Vector<String> obj;
62
63 bool is_shared = HasFlag("SO");
64 String shared_ext = (HasFlag("WIN32") ? ".dll" : ".so");
65
66 Vector<String> sfile, isfile;
67 Vector<String> soptions, isoptions;
68 bool error = false;
69
70 String pch_header;
71
72 Index<String> nopch, noblitz;
73
74 bool blitz = HasFlag("BLITZ");
75 bool release = !HasFlag("DEBUG");
76 bool objectivec = HasFlag("OBJC");
77
78 if(HasFlag("OSX"))
79 objectivec = true;
80
81 for(i = 0; i < pkg.GetCount(); i++) {
82 if(!IdeIsBuilding())
83 return false;
84 if(!pkg[i].separator) {
85 String gop = Gather(pkg[i].option, config.GetKeys());
86 Vector<String> srcfile = CustomStep(pkg[i], package, error);
87 if(srcfile.GetCount() == 0)
88 error = true;
89 for(int j = 0; j < srcfile.GetCount(); j++) {
90 String fn = srcfile[j];
91 String ext = GetSrcType(fn);
92 if(findarg(ext, ".c", ".cpp", ".cc", ".cxx", ".brc", ".s", ".ss") >= 0 ||
93 objectivec && findarg(ext, ".mm", ".m") >= 0 ||
94 +
95 (!release && blitz && ext == ".icpp") ||
96 ext == ".rc" && HasFlag("WIN32")) {
97 sfile.Add(fn);
98 soptions.Add(gop);
99 }
100 else
101 if(ext == ".icpp") {
102 isfile.Add(fn);
103 isoptions.Add(gop);
104 }
105 else
106 if(ext == ".o")
107 obj.Add(fn);
108 else
109 if(ext == ".a" || ext == ".so")
110 linkfile.Add(fn);
111 else
112 if(IsHeaderExt(ext) && pkg[i].pch && allow_pch && !blitz) {
113 if(pch_header.GetCount())
114 PutConsole(GetFileName(fn) + ": multiple PCHs are not allowed. Check your package configuration");
115 else
116 pch_header = fn;
117 }
118 if(pkg[i].nopch) {
119 nopch.Add(fn);
120 if(allow_pch && release)
121 noblitz.Add(fn);
122 }
123 if(pkg[i].noblitz)
124 noblitz.Add(fn);
125 if(ext == ".c")
126 nopch.Add(fn);
127 }
128 }
129 }
130
131 String cc = CmdLine(package, pkg);
132
133 // if(IsVerbose())
134 // cc << " -v";
135 if(HasFlag("WIN32")/* && HasFlag("MT")*/)
136 cc << " -mthreads";
137
138 if(HasFlag("DEBUG_MINIMAL") || HasFlag("DEBUG_FULL")) {
139 cc << (HasFlag("WIN32") && HasFlag("CLANG") ? " -gcodeview -fno-limit-debug-info" : " -ggdb");
140 cc << (HasFlag("DEBUG_FULL") ? " -g2" : " -g1");
141 }
142 String fuse_cxa_atexit;
143 if(is_shared /*&& !HasFlag("MAIN")*/) {
144 cc << " -shared -fPIC";
145 fuse_cxa_atexit = " -fuse-cxa-atexit";
146 }
147 if(!HasFlag("SHARED") && !is_shared)
148 cc << " -static ";
149 // else if(!HasFlag("WIN32")) // TRC 05/03/08: dynamic fPIC doesn't seem to work in MinGW
150 // cc << " -dynamic -fPIC "; // TRC 05/03/30: dynamic fPIC doesn't seem to work in GCC either :-)
151 cc << ' ' << Gather(pkg.option, config.GetKeys());
152 cc << " -fexceptions";
153
154 #if 0
155 if (HasFlag("OSX")) {
156 if (HasFlag("POWERPC"))
157 cc << " -arch ppc";
158 if (HasFlag("X86"))
159 cc << " -arch i386";
160 }
161 #endif
162 // if(HasFlag("SSE2")) {
163 // cc << " -msse2";
164 // if(!HasFlag("CLANG"))
165 // cc << " -mfpmath=sse";
166 // }
167
168 if(!release)
169 cc << " -D_DEBUG " << debug_options;
170 else
171 cc << ' ' << release_options;
172
173 if(pkg.nowarnings)
174 cc << " -w";
175
176 int recompile = 0;
177 Blitz b;
178 if(blitz) {
179 BlitzBuilderComponent bc(this);
180 b = bc.MakeBlitzStep(sfile, soptions, obj, immfile, ".o", noblitz, package);
181 recompile = b.build;
182 }
183
184 for(i = 0; i < sfile.GetCount(); i++) {
185 String fn = sfile[i];
186 String ext = ToLower(GetFileExt(fn));
187 if(findarg(ext, ".rc", ".brc", ".c") < 0 && HdependFileTime(sfile[i]) > GetFileTime(CatAnyPath(outdir, GetFileTitle(fn) + ".o")))
188 recompile++;
189 }
190
191 String pch_use;
192 String pch_file;
193
194 if(recompile > 2 && pch_header.GetCount()) {
195 String pch_header2 = CatAnyPath(outdir, GetFileTitle(pch_header) + "$pch.h");
196 pch_file = pch_header2 + ".gch";
197 SaveFile(pch_header2, "#include <" + pch_header + ">"); // CLANG needs a copy of header
198
199 int pch_slot = AllocSlot();
200 StringBuffer sb;
201
202 sb << Join(cc, cpp_options) << " -x c++-header " << GetHostPathQ(pch_header) << " -o " << GetHostPathQ(pch_file);
203
204 PutConsole("Precompiling header: " + GetFileName(pch_header));
205 if(pch_slot < 0 || !Run(~sb, pch_slot, GetHostPath(pch_file), 1))
206 error = true;
207 Wait();
208
209 pch_use = " -I" + GetHostPathQ(outdir) + " -include " + GetFileName(pch_header2) + " -Winvalid-pch ";
210 }
211
212 if(blitz && b.build) {
213 PutConsole("BLITZ:" + b.info);
214 int slot = AllocSlot();
215 if(slot < 0 || !Run(String().Cat() << Join(cc, cpp_options) << ' '
216 << GetHostPathQ(b.path)
217 << " -o " << GetHostPathQ(b.object), slot, GetHostPath(b.object), b.count))
218 error = true;
219 }
220
221 int first_ifile = sfile.GetCount();
222 sfile.AppendPick(pick(isfile));
223 soptions.AppendPick(pick(isoptions));
224
225 int ccount = 0;
226 for(i = 0; i < sfile.GetCount(); i++) {
227 if(!IdeIsBuilding())
228 return false;
229 String fn = sfile[i];
230 String ext = ToLower(GetFileExt(fn));
231 bool rc = (ext == ".rc");
232 bool brc = (ext == ".brc");
233 bool init = (i >= first_ifile);
234 String objfile = CatAnyPath(outdir, GetFileTitle(fn) + (rc ? "$rc.o" : brc ? "$brc.o" : ".o"));
235 if(GetFileName(fn) == "Info.plist")
236 Info_plist = LoadFile(fn);
237 if(HdependFileTime(fn) > GetFileTime(GetHostPath(objfile))) {
238 PutConsole(GetFileName(fn));
239 int time = msecs();
240 bool execerr = false;
241 if(rc) {
242 String exec;
243 String windres = "windres.exe";
244 int q = compiler.ReverseFind('-'); // clang32 windres name is i686-w64-mingw32-windres.exe
245 if(q > 0)
246 windres = compiler.Mid(0, q + 1) + windres;
247 exec << GetHostPath(FindInDirs(host->GetExecutablesDirs(), windres)) << " -i " << GetHostPathQ(fn);
248 if(cc.Find(" -m32 ") >= 0)
249 exec << " --target=pe-i386 ";
250 exec << " -o " << GetHostPathQ(objfile) << Includes(" --include-dir=", package, pkg)
251 << DefinesTargetTime(" -D", package, pkg) + (HasFlag("DEBUG")?" -D_DEBUG":"");
252 PutVerbose(exec);
253 int slot = AllocSlot();
254 execerr = (slot < 0 || !Run(exec, slot, GetHostPath(objfile), 1));
255 }
256 else if(brc) {
257 try {
258 String brcdata = LoadFile(fn);
259 if(brcdata.IsVoid())
260 throw Exc(Format("error reading file '%s'", fn));
261 CParser parser(brcdata, fn);
262 BinaryToObject(GetHostPath(objfile), parser, GetFileDirectory(fn), package, pkg);
263 }
264 catch(Exc e) {
265 PutConsole(e);
266 execerr = true;
267 }
268 }
269 else {
270 String exec = cc;
271 if(ext == ".c")
272 exec << Join(" -x c", c_options) << ' ';
273 else if(ext == ".s" || ext == ".S")
274 exec << " -x assembler-with-cpp ";
275 else
276 if (ext == ".m")
277 exec << fuse_cxa_atexit << " -x objective-c ";
278 else
279 if (ext == ".mm")
280 exec << fuse_cxa_atexit << Join(" -x objective-c++ ", cpp_options) << ' ';
281 else {
282 exec << fuse_cxa_atexit << Join(" -x c++", cpp_options) << ' ';
283 exec << pch_use;
284 }
285 exec << GetHostPathQ(fn) << " " << soptions[i] << " -o " << GetHostPathQ(objfile);
286 PutVerbose(exec);
287 int slot = AllocSlot();
288 execerr = (slot < 0 || !Run(exec, slot, GetHostPath(objfile), 1));
289 }
290 if(execerr)
291 DeleteFile(objfile);
292 error |= execerr;
293 PutVerbose("compiled in " + GetPrintTime(time));
294 ccount++;
295 }
296 immfile.Add(objfile);
297 if(init)
298 linkfile.Add(objfile);
299 else
300 obj.Add(objfile);
301 }
302
303 if(error) {
304 // if(ccount)
305 // PutCompileTime(time, ccount);
306 IdeConsoleEndGroup();
307 return false;
308 }
309
310 MergeWith(linkoptions, " ", Gather(pkg.link, config.GetKeys()));
311 if(linkoptions.GetCount())
312 linkoptions << ' ';
313
314 Vector<String> libs = Split(Gather(pkg.library, config.GetKeys()), ' ');
315 linkfile.Append(libs);
316
317 if(pch_file.GetCount())
318 OnFinish(callback1(DeletePCHFile, pch_file));
319
320 int libtime = msecs();
321 if(!HasFlag("MAIN")) {
322 if(HasFlag("BLITZ") && !HasFlag("SO") || HasFlag("NOLIB")) {
323 linkfile.Append(obj); // Simply link everything as .o files...
324 IdeConsoleEndGroup();
325 // if(ccount)
326 // PutCompileTime(time, ccount);
327 return true;
328 }
329 IdeConsoleEndGroup();
330 if(!Wait())
331 return false;
332 String product;
333 if(is_shared)
334 product = GetSharedLibPath(package);
335 else
336 product = CatAnyPath(outdir, GetAnyFileName(package) + ".a");
337 String hproduct = GetHostPath(product);
338 Time producttime = GetFileTime(hproduct);
339 // LOG("hproduct = " << hproduct << ", time = " << producttime);
340 if(obj.GetCount()) {
341 linkfile.Add(GetHostPath(product));
342 immfile.Add(GetHostPath(product));
343 }
344 for(int i = 0; i < obj.GetCount(); i++)
345 if(GetFileTime(obj[i]) > producttime) {
346 String lib;
347 if(is_shared) {
348 lib = CompilerName();
349 lib << " -shared -fPIC -fuse-cxa-atexit";
350 if(!HasFlag("SHARED") && !is_shared)
351 lib << " -static";
352 // else if(!HasFlag("WIN32")) // TRC 05/03/08: dynamic fPIC causes trouble in MinGW
353 // lib << " -dynamic -fPIC"; // TRC 05/03/30: dynamic fPIC doesn't seem to work in GCC either :-)
354 if(HasFlag("GCC32"))
355 lib << " -m32";
356 Point p = ExtractVersion();
357 if(!IsNull(p.x) && HasFlag("WIN32")) {
358 lib << " -Xlinker --major-image-version -Xlinker " << p.x;
359 if(!IsNull(p.y))
360 lib << " -Xlinker --minor-image-version -Xlinker " << p.y;
361 }
362 lib << ' ' << Gather(pkg.link, config.GetKeys());
363
364 lib << " -o ";
365 }
366 else
367 lib = "ar -sr ";
368 lib << GetHostPathQ(product);
369
370
371 String llib;
372 for(int i = 0; i < obj.GetCount(); i++)
373 llib << ' ' << GetHostPathQ(obj[i]);
374 PutConsole("Creating library...");
375 DeleteFile(hproduct);
376 if(is_shared) {
377 for(int i = 0; i < libpath.GetCount(); i++)
378 llib << " -L" << GetHostPathQ(libpath[i]);
379 for(int i = 0; i < all_uses.GetCount(); i++)
380 llib << ' ' << GetHostPathQ(GetSharedLibPath(all_uses[i]));
381 for(int i = 0; i < all_libraries.GetCount(); i++)
382 llib << " -l" << GetHostPathQ(all_libraries[i]);
383
384 if(HasFlag("POSIX"))
385 llib << " -Wl,-soname," << GetSoname(product);
386 }
387
388 String tmpFileName;
389 if(HasFlag("LINUX") || HasFlag("WIN32")) {
390 if(lib.GetCount() + llib.GetCount() >= 8192)
391 {
392 tmpFileName = GetTempFileName();
393 // we can't simply put all data on a single line
394 // as it has a limit of around 130000 chars too, so we split
395 // in multiple lines
396 FileOut f(tmpFileName);
397 while(llib != "")
398 {
399 int found = 0;
400 bool quotes = false;
401 int lim = min(8192, llib.GetCount());
402 for(int i = 0; i < lim; i++)
403 {
404 char c = llib[i];
405 if(isspace(c) && !quotes)
406 found = i;
407 else if(c == '"')
408 quotes = !quotes;
409 }
410 if(!found)
411 found = llib.GetCount();
412
413 // replace all '\' with '/'`
414 llib = UnixPath(llib);
415
416 f.PutLine(llib.Left(found));
417 llib.Remove(0, found);
418 }
419 f.Close();
420 lib << " @" << tmpFileName;
421 }
422 else
423 lib << llib;
424 }
425 else
426 lib << llib;
427
428 int res = Execute(lib);
429 if(tmpFileName.GetCount())
430 FileDelete(tmpFileName);
431 String folder, libF, soF, linkF;
432 if(HasFlag("POSIX")) {
433 if(is_shared)
434 {
435 folder = GetFileFolder(hproduct);
436 libF = GetFileName(hproduct);
437 soF = AppendFileName(folder, GetSoname(hproduct));
438 linkF = AppendFileName(folder, GetSoLinkName(hproduct));
439 }
440 }
441 if(res) {
442 DeleteFile(hproduct);
443 if(HasFlag("POSIX")) {
444 if(is_shared) {
445 DeleteFile(libF);
446 DeleteFile(linkF);
447 }
448 }
449 return false;
450 }
451 #ifdef PLATFORM_POSIX // we do not have symlink in Win32....
452 if(HasFlag("POSIX")) {
453 if(is_shared)
454 {
455 int r;
456 r = symlink(libF, soF);
457 r = symlink(libF, linkF);
458 (void)r;
459 }
460 }
461 #endif
462 PutConsole(String().Cat() << hproduct << " (" << GetFileInfo(hproduct).length
463 << " B) created in " << GetPrintTime(libtime));
464 break;
465 }
466 return true;
467 }
468
469 IdeConsoleEndGroup();
470 obj.Append(linkfile);
471 linkfile = pick(obj);
472 return true;
473 }
474
475
Link(const Vector<String> & linkfile,const String & linkoptions,bool createmap)476 bool GccBuilder::Link(const Vector<String>& linkfile, const String& linkoptions, bool createmap)
477 {
478 if(!Wait())
479 return false;
480 PutLinking();
481 int time = msecs();
482 #ifdef PLATFORM_OSX
483 CocoaAppBundle();
484 #endif
485 for(int i = 0; i < linkfile.GetCount(); i++)
486 if(GetFileTime(linkfile[i]) > targettime) {
487 Vector<String> lib;
488 String lnk = CompilerName();
489 // if(IsVerbose())
490 // lnk << " -v";
491 if(HasFlag("GCC32"))
492 lnk << " -m32";
493 if(HasFlag("DLL"))
494 lnk << " -shared";
495 if(!HasFlag("SHARED") && !HasFlag("SO"))
496 lnk << " -static";
497 if(HasFlag("WINCE"))
498 lnk << " -mwindowsce";
499 else if(HasFlag("WIN32")) {
500 lnk << " -mthreads";
501 if(HasFlag("CLANG")) {
502 if(HasFlag("GUI"))
503 lnk << " -mwindows";
504 else
505 lnk << " -mconsole";
506 }
507 else {
508 lnk << " -mwindows";
509 // if(HasFlag("MT"))
510 if(!HasFlag("GUI"))
511 lnk << " -mconsole";
512 }
513 }
514 lnk << " -o " << GetHostPathQ(target);
515 if(createmap)
516 lnk << " -Wl,-Map," << GetHostPathQ(GetFileDirectory(target) + GetFileTitle(target) + ".map");
517 if(HasFlag("DEBUG_MINIMAL") || HasFlag("DEBUG_FULL"))
518 lnk << (HasFlag("CLANG") && HasFlag("WIN32") ? " -Wl,-pdb=" : " -ggdb");
519 else
520 lnk << (!HasFlag("OSX") ? " -Wl,-s" : "");
521 for(i = 0; i < libpath.GetCount(); i++)
522 lnk << " -L" << GetHostPathQ(libpath[i]);
523 MergeWith(lnk, " ", linkoptions);
524 String lfilename;
525 if(HasFlag("OBJC")) {
526 String lfilename;
527 String linklist;
528 for(i = 0; i < linkfile.GetCount(); i++)
529 if(ToLower(GetFileExt(linkfile[i])) == ".o" || ToLower(GetFileExt(linkfile[i])) == ".a")
530 linklist << GetHostPath(linkfile[i]) << '\n';
531
532 String linklistM = "Producing link file list ...\n";
533 String odir = GetFileDirectory(linkfile[0]);
534 lfilename << GetHostPath(GetFileFolder(linkfile[0])) << ".LinkFileList";
535
536 linklistM << lfilename;
537 UPP::SaveFile(lfilename, linklist);
538 lnk << " -L" << GetHostPathQ(odir)
539 << " -F" << GetHostPathQ(odir)
540 << " -filelist " << lfilename << " ";
541 PutConsole( linklistM );
542 }
543 else
544 for(i = 0; i < linkfile.GetCount(); i++) {
545 if(ToLower(GetFileExt(linkfile[i])) == ".o")
546 lnk << ' ' << GetHostPathQ(linkfile[i]);
547 else
548 lib.Add(linkfile[i]);
549 }
550 if(!HasFlag("SOLARIS") && !HasFlag("OSX") && !HasFlag("OBJC"))
551 lnk << " -Wl,--start-group ";
552 for(String s : pkg_config)
553 lnk << " `pkg-config --libs " << s << "`";
554 for(int pass = 0; pass < 2; pass++) {
555 for(i = 0; i < lib.GetCount(); i++) {
556 String ln = lib[i];
557 String ext = ToLower(GetFileExt(ln));
558
559 // unix shared libs shall have version number AFTER .so (sic)
560 // so we shall find the true extension....
561 if(HasFlag("POSIX") && ext != ".so")
562 {
563 const char *c = ln.Last();
564 while(--c >= ~ln)
565 if(!IsDigit(*c) && *c != '.')
566 break;
567 int pos = int(c - ~ln - 2);
568 if(pos >= 0 && ToLower(ln.Mid(pos, 3)) == ".so")
569 ext = ".so";
570 }
571
572 if(pass == 0) {
573 if(ext == ".a")
574 lnk << ' ' << GetHostPathQ(FindInDirs(libpath, lib[i]));
575 }
576 else
577 if(ext != ".a") {
578 if(ext == ".so" || ext == ".dll" || ext == ".lib")
579 lnk << ' ' << GetHostPathQ(FindInDirs(libpath, lib[i]));
580 else
581 lnk << " -l" << ln;
582 }
583 }
584 if(pass == 1 && !HasFlag("SOLARIS") && !HasFlag("OSX"))
585 lnk << " -Wl,--end-group";
586 }
587 PutConsole("Linking...");
588 bool error = false;
589 CustomStep(".pre-link", Null, error);
590 if(!error && Execute(lnk) == 0) {
591 CustomStep(".post-link", Null, error);
592 PutConsole(String().Cat() << GetHostPath(target) << " (" << GetFileInfo(target).length
593 << " B) linked in " << GetPrintTime(time));
594 return !error;
595 }
596 else {
597 DeleteFile(target);
598 return false;
599 }
600 }
601
602 PutConsole(String().Cat() << GetHostPath(target) << " (" << GetFileInfo(target).length
603 << " B) is up to date.");
604 return true;
605 }
606
Preprocess(const String & package,const String & file,const String & target,bool asmout)607 bool GccBuilder::Preprocess(const String& package, const String& file, const String& target, bool asmout)
608 {
609 Package pkg;
610 String packagePath = PackagePath(package);
611 pkg.Load(packagePath);
612 String packageDir = GetFileFolder(packagePath);
613 ChDir(packageDir);
614 PutVerbose("cd " + packageDir);
615
616 String cmd = CmdLine(package, pkg);
617 cmd << " " << Gather(pkg.option, config.GetKeys());
618 cmd << " -o " << target;
619 cmd << (asmout ? " -S " : " -E ") << GetHostPathQ(file);
620 if(BuilderUtils::IsCFile(file))
621 cmd << " " << c_options;
622 else
623 if(BuilderUtils::IsCppFile(file))
624 cmd << " " << cpp_options;
625 return Execute(cmd);
626 }
627
CreateGccBuilder()628 Builder *CreateGccBuilder()
629 {
630 return new GccBuilder;
631 }
632
INITIALIZER(GccBuilder)633 INITIALIZER(GccBuilder)
634 {
635 RegisterBuilder("GCC", CreateGccBuilder);
636 RegisterBuilder("CLANG", CreateGccBuilder);
637 }
638