[RTL]
[reactos.git] / reactos / tools / rbuild / rbuild.cpp
1 /*
2 * Copyright (C) 2005 Casper S. Hornstrup
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 #include "pch.h"
19 #include <typeinfo>
20 #include <algorithm>
21
22 #include <stdio.h>
23 #ifdef WIN32
24 #include <io.h>
25 #endif
26 #include <assert.h>
27
28 #include "rbuild.h"
29 #include "backend/backend.h"
30 #include "backend/mingw/mingw.h"
31
32 using std::string;
33 using std::vector;
34
35 static string BuildSystem;
36 static string RootXmlFile;
37 static Configuration configuration;
38 static std::map<string, string> properties;
39
40 bool
41 ParseAutomaticDependencySwitch (
42 char switchChar2,
43 char* switchStart )
44 {
45 switch ( switchChar2 )
46 {
47 case 'd':
48 configuration.Dependencies = NoDependencies;
49 break;
50 case 'a':
51 configuration.Dependencies = AutomaticDependencies;
52 break;
53 case 'f':
54 configuration.Dependencies = FullDependencies;
55 break;
56 case 'm':
57 if ( strlen ( switchStart ) <= 3 )
58 {
59 printf ( "Switch -dm requires a module name\n" );
60 return false;
61 }
62 configuration.CheckDependenciesForModuleOnly = true;
63 configuration.CheckDependenciesForModuleOnlyModule = string(&switchStart[3]);
64 break;
65 default:
66 printf ( "Unknown switch -d%c\n",
67 switchChar2 );
68 return false;
69 }
70 return true;
71 }
72
73 bool
74 ParseCompilationUnitSwitch (
75 char switchChar2,
76 char* switchStart )
77 {
78 switch ( switchChar2 )
79 {
80 case 'd':
81 configuration.CompilationUnitsEnabled = false;
82 break;
83 default:
84 printf ( "Unknown switch -u%c\n",
85 switchChar2 );
86 return false;
87 }
88 return true;
89 }
90
91 bool
92 ParsePrecompiledHeaderSwitch (
93 char switchChar2,
94 char* switchStart )
95 {
96 switch ( switchChar2 )
97 {
98 case 'd':
99 configuration.PrecompiledHeadersEnabled = false;
100 break;
101 default:
102 printf ( "Unknown switch -h%c\n",
103 switchChar2 );
104 return false;
105 }
106 return true;
107 }
108
109 bool
110 ParseVCProjectSwitch (
111 char switchChar2,
112 char* switchStart )
113 {
114 string temp;
115
116 switch ( switchChar2 )
117 {
118 case 's':
119 if ( strlen ( switchStart ) <= 3 )
120 {
121 printf ( "Switch -dm requires a module name\n" );
122 return false;
123 }
124 configuration.VSProjectVersion = string(&switchStart[3]);
125
126 if (configuration.VSProjectVersion.at(0) == '{') {
127 printf ( "Error: invalid char {\n" );
128 return false;
129 }
130
131 if (configuration.VSProjectVersion.length() == 1) //7,8
132 configuration.VSProjectVersion.append(".00");
133
134 if (configuration.VSProjectVersion.length() == 3) //7.1
135 configuration.VSProjectVersion.append("0");
136
137 //We should set this here because in the end we will use
138 //msc sompiler so we need to parse msc specidic
139 //definitions and includes
140 configuration.Compiler = MicrosoftC;
141
142 break;
143 case 'c':
144 configuration.VSConfigurationType = string (&switchStart[3]);
145 configuration.InstallFiles = true;
146 break;
147 case 'o':
148 if ( strlen ( switchStart ) <= 3 )
149 {
150 printf ( "Invalid switch\n" );
151 return false;
152 }
153 temp = string (&switchStart[3]);
154 if ( temp.find ("configuration") != string::npos )
155 configuration.UseConfigurationInPath = true;
156
157 if ( temp.find ("version") != string::npos )
158 configuration.UseVSVersionInPath = true;
159 break;
160 default:
161 printf ( "Unknown switch -d%c\n",
162 switchChar2 );
163 return false;
164 }
165 return true;
166 }
167
168 bool
169 ParseMingwSwitch ( char* switchStart )
170 {
171 switchStart += 2;
172
173 if ( *switchStart == 'c' )
174 {
175 ++ switchStart;
176
177 if ( strcmp ( switchStart, "msc" ) == 0 )
178 configuration.Compiler = MicrosoftC;
179 else if ( strcmp ( switchStart, "gcc" ) == 0 )
180 configuration.Compiler = GnuGcc;
181 else
182 {
183 printf ( "Unknown value of -Mc: %s\n", switchStart );
184 return false;
185 }
186 }
187 else if ( *switchStart == 'l' )
188 {
189 ++ switchStart;
190
191 if ( strcmp ( switchStart, "mslink" ) == 0 )
192 configuration.Linker = MicrosoftLink;
193 else if ( strcmp ( switchStart, "ld" ) == 0 )
194 configuration.Linker = GnuLd;
195 else
196 {
197 printf ( "Unknown value of -Ml: %s\n", switchStart );
198 return false;
199 }
200 }
201 else if ( strcmp ( switchStart, "microsoft" ) == 0 )
202 {
203 configuration.Compiler = MicrosoftC;
204 configuration.Linker = MicrosoftLink;
205 }
206 else if ( strcmp ( switchStart, "gnu" ) == 0 )
207 {
208 configuration.Compiler = GnuGcc;
209 configuration.Linker = GnuLd;
210 }
211 else
212 {
213 printf ( "Unknown value of -M: %s\n", switchStart );
214 return false;
215 }
216
217 return true;
218 }
219
220 bool
221 ParseMakeSwitch ( char switchChar2 )
222 {
223 switch ( switchChar2 )
224 {
225 case 'i':
226 configuration.MakeHandlesInstallDirectories = true;
227 break;
228 default:
229 printf ( "Unknown switch -m%c\n",
230 switchChar2 );
231 return false;
232 }
233 return true;
234 }
235
236 bool
237 ParseProxyMakefileSwitch ( char switchChar2 )
238 {
239 switch ( switchChar2 )
240 {
241 case 's':
242 configuration.GenerateProxyMakefilesInSourceTree = true;
243 break;
244 default:
245 printf ( "Unknown switch -p%c\n",
246 switchChar2 );
247 return false;
248 }
249 return true;
250 }
251
252 bool
253 ParseDefineSwitch ( char* switchStart )
254 {
255 string s = string ( switchStart + 2 );
256 string::size_type separator = s.find ( '=' );
257 if ( separator == string::npos || separator == 0 )
258 {
259 printf ( "Invalid define switch: '%s'\n", switchStart );
260 return false;
261 }
262 if ( s.find ( '=', separator + 1 ) != string::npos )
263 {
264 printf ( "Invalid define switch: '%s'\n", switchStart );
265 return false;
266 }
267
268 string var = s.substr ( 0, separator );
269 string val = s.substr ( separator + 1 );
270 properties.insert ( std::pair<string, string> ( var, val ) );
271 return true;
272 }
273
274 bool
275 ParseSwitch ( int argc, char** argv, int index )
276 {
277 char switchChar = strlen ( argv[index] ) > 1 ? argv[index][1] : ' ';
278 char switchChar2 = strlen ( argv[index] ) > 2 ? argv[index][2] : ' ';
279 switch ( switchChar )
280 {
281 case 'v':
282 if (switchChar2 == 's' || switchChar2 == 'c' || switchChar2 == 'o')
283 {
284 return ParseVCProjectSwitch (
285 switchChar2,
286 argv[index] );
287 }
288 else
289 configuration.Verbose = true;
290 break;
291 case 'c':
292 configuration.CleanAsYouGo = true;
293 break;
294 case 'd':
295 return ParseAutomaticDependencySwitch (
296 switchChar2,
297 argv[index] );
298 case 'h':
299 return ParsePrecompiledHeaderSwitch (
300 switchChar2,
301 argv[index] );
302
303 case 'u':
304 return ParseCompilationUnitSwitch (
305 switchChar2,
306 argv[index] );
307 case 'r':
308 RootXmlFile = string(&argv[index][2]);
309 break;
310 case 'm':
311 return ParseMakeSwitch ( switchChar2 );
312 case 'M':
313 return ParseMingwSwitch ( argv[index] );
314 case 'p':
315 return ParseProxyMakefileSwitch ( switchChar2 );
316 case 'D':
317 return ParseDefineSwitch ( argv[index] );
318 default:
319 printf (
320 "Unknown switch -%c\n",
321 switchChar );
322 return false;
323 }
324 return true;
325 }
326
327 bool
328 ParseArguments ( int argc, char** argv )
329 {
330 if ( argc < 2 )
331 return false;
332
333 for ( int i = 1; i < argc; i++ )
334 {
335 if ( argv[i][0] == '-' )
336 {
337 if ( !ParseSwitch ( argc, argv, i ) )
338 return false;
339 }
340 else
341 BuildSystem = argv[i];
342 }
343
344 return true;
345 }
346
347 int
348 main ( int argc, char** argv )
349 {
350 InitializeEnvironment ();
351
352 if ( !ParseArguments ( argc, argv ) )
353 {
354 printf ( "Generates project files for buildsystems\n\n" );
355 printf ( " rbuild [switches] -r{rootfile.rbuild} buildsystem\n\n" );
356 printf ( "Switches:\n" );
357 printf ( " -v Be verbose.\n" );
358 printf ( " -c Clean as you go. Delete generated files as soon as they are not\n" );
359 printf ( " needed anymore.\n" );
360 printf ( " -dd Disable automatic dependencies.\n" );
361 printf ( " -da Enable automatic dependencies.\n" );
362 printf ( " -df Enable full dependencies.\n" );
363 printf ( " -dm{module} Check only automatic dependencies for this module.\n" );
364 printf ( " -ud Disable multiple source files per compilation unit.\n" );
365 printf ( " -mi Let make handle creation of install directories. Rbuild will\n" );
366 printf ( " not generate the directories.\n" );
367 printf ( " -ps Generate proxy makefiles in source tree instead of the output.\n" );
368 printf ( " tree.\n" );
369 printf ( " -vs{version} Version of MS VS project files. Default is %s.\n", MS_VS_DEF_VERSION );
370 printf ( " -vo{version|configuration} Adds subdirectory path to the default Intermediate-Outputdirectory.\n" );
371 printf ( " -Mc{compiler} Compiler to use for mingw backend. Can be one of:\n" );
372 printf ( " %-10s %s (default)\n", "gcc", "GNU compiler collection (gcc, g++)\n");
373 printf ( " %-10s %s\n", "msc", "Microsoft Visual C++ (cl)\n");
374 printf ( " -Ml{compiler} Linker to use for mingw backend. Can be one of:\n" );
375 printf ( " %-10s %s (default)\n", "ld", "GNU binutils (ld, dlltool)\n");
376 printf ( " %-10s %s\n", "mslink", "Microsoft Linker (link, lib)\n");
377 printf ( " -Mmicrosoft Same as -Mcmsc -Mlmslink\n" );
378 printf ( " -Mgnu Same as -Mcgcc -Mlld\n" );
379 printf ( " -Dvar=val Set the value of 'var' variable to 'val'.\n" );
380 printf ( "\n" );
381 printf ( " buildsystem Target build system. Can be one of:\n" );
382
383 std::map<std::string,Backend::Factory*>::iterator iter;
384 for (iter = Backend::Factory::map_begin(); iter != Backend::Factory::map_end(); iter++)
385 {
386 Backend::Factory *factory = iter->second;
387 printf ( " %-10s %s\n", factory->Name(), factory->Description());
388 }
389 return 1;
390 }
391 try
392 {
393 if ( RootXmlFile.length () == 0 )
394 throw MissingArgumentException ( "-r" );
395
396 string projectFilename ( RootXmlFile );
397
398 printf ( "Reading build files..." );
399 Project project ( configuration, projectFilename, &properties );
400 printf ( "done\n" );
401
402 project.SetBackend ( Backend::Factory::Create (
403 BuildSystem,
404 project,
405 configuration ) );
406
407 project.ExecuteInvocations ();
408 project.GetBackend().Process();
409
410 return 0;
411 }
412 catch ( Exception& ex )
413 {
414 printf ( "%s\n", (*ex).c_str () );
415 return 1;
416 }
417 catch ( XMLException& ex )
418 {
419 printf ( "%s\n", (*ex).c_str () );
420 return 1;
421 }
422 }