[RTL]
[reactos.git] / reactos / tools / rbuild / backend / msvc / msvc.cpp
1 /*
2 * Copyright (C) 2005 Trevor McCort
3 * Copyright (C) 2005 Casper S. Hornstrup
4 * Copyright (C) 2005 Steven Edwards
5 * Copyright (C) 2005 Royce Mitchell
6 * Copyright (C) 2006 Christoph von Wittich
7 * Copyright (C) 2009 Ged Murphy
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23 #ifdef _MSC_VER
24 #pragma warning ( disable : 4786 )
25 #pragma warning ( disable : 4996 )
26 #endif//_MSC_VER
27
28 #include <iostream>
29 #include <fstream>
30 #include <string>
31 #include <vector>
32
33 #include "msvc.h"
34
35 using std::string;
36 using std::vector;
37 using std::ifstream;
38
39 static class MSVCFactory : public Backend::Factory
40 {
41 public:
42
43 MSVCFactory() : Factory("MSVC", "Microsoft Visual C") {}
44 Backend *operator() (Project &project,
45 Configuration& configuration)
46 {
47 return new MSVCBackend(project, configuration);
48 }
49
50 } factory;
51
52 MSVCConfiguration::MSVCConfiguration ( const OptimizationType optimization, const HeadersType headers, const std::string &name )
53 {
54 this->optimization = optimization;
55 this->headers = headers;
56 if ( name != "" )
57 this->name = name;
58 else
59 {
60 std::string headers_name;
61 if ( headers == MSVCHeaders )
62 headers_name = "";
63 else
64 headers_name = " - ReactOS headers";
65 if ( optimization == Debug )
66 this->name = "Debug" + headers_name;
67 else if ( optimization == Release )
68 this->name = "Release" + headers_name;
69 else if ( optimization == Speed )
70 this->name = "Speed" + headers_name;
71 else if ( optimization == RosBuild )
72 this->name = "RosBuild";
73 else
74 this->name = "Unknown" + headers_name;
75 }
76 }
77
78 MSVCBackend::MSVCBackend(Project &project,
79 Configuration& configuration) : Backend(project, configuration)
80 {
81 m_unitCount = 0;
82 }
83
84 void MSVCBackend::Process()
85 {
86 while ( m_configurations.size () > 0 )
87 {
88 const MSVCConfiguration* cfg = m_configurations.back();
89 m_configurations.pop_back();
90 delete cfg;
91 }
92
93 //Don't generate configurations that require WDK if it can't be found
94 if(getenv ( "BASEDIR" ) != NULL)
95 {
96 m_configurations.push_back ( new MSVCConfiguration( Debug ));
97 m_configurations.push_back ( new MSVCConfiguration( Release ));
98 }
99 m_configurations.push_back ( new MSVCConfiguration( RosBuild ));
100 m_configurations.push_back ( new MSVCConfiguration( Debug, ReactOSHeaders ));
101 m_configurations.push_back ( new MSVCConfiguration( Release, ReactOSHeaders ));
102 // m_configurations.push_back ( new MSVCConfiguration( Speed ));
103 // m_configurations.push_back ( new MSVCConfiguration( Speed, ReactOSHeaders ));
104
105 if ( configuration.CleanAsYouGo ) {
106 _clean_project_files();
107 return;
108 }
109 if ( configuration.InstallFiles ) {
110 _install_files( _get_vc_dir(), configuration.VSConfigurationType );
111 return;
112 }
113 string filename_sln ( ProjectNode.name );
114
115 filename_sln += "_auto.sln";
116 printf ( "Creating MSVC workspace: %s\n", filename_sln.c_str() );
117
118 if (configuration.VSProjectVersion == "10.00")
119 {
120 PropsMaker propsMaker( &ProjectNode, "reactos.props", m_configurations );
121 propsMaker._generate_props( _get_solution_version(), _get_studio_version() );
122 }
123 else
124 {
125 //Write a property page for each configuration
126 for ( size_t icfg = 0; icfg < m_configurations.size(); icfg++ )
127 {
128 MSVCConfiguration* cfg = m_configurations[icfg];
129
130 //RosBuild doesn't need a property page
131 if(cfg->optimization == RosBuild)
132 continue;
133
134 //Write the propery pages files
135 string filename_props( cfg->name );
136
137 filename_props = filename_props + ".vsprops";
138 VSPropsMaker propsMaker( configuration, &ProjectNode, filename_props, cfg );
139 propsMaker._generate_props( _get_solution_version(), _get_studio_version() );
140 }
141 }
142 // Write out the project files
143 ProcessModules();
144
145 printf ( "Done.\n" );
146 }
147
148 void MSVCBackend::ProcessModules()
149 {
150 string filename_sln ( ProjectNode.name );
151
152 filename_sln += "_auto.sln";
153
154 // Write the solution file
155 SlnMaker slnMaker( configuration, m_configurations, filename_sln, _get_solution_version(), _get_studio_version() );
156
157 for(std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin(); p != ProjectNode.modules.end(); ++ p)
158 {
159 Module &module = *p->second;
160
161 module.guid = _gen_guid();
162
163 ProjMaker *projMaker;
164
165 if (configuration.VSProjectVersion == "10.00")
166 {
167 string vcxproj_file = VcxprojFileName(module);
168 projMaker = new VCXProjMaker( configuration, m_configurations, vcxproj_file, module );
169 }
170 else
171 {
172 string vcproj_file = VcprojFileName(module);
173 projMaker = new VCProjMaker( configuration, m_configurations, vcproj_file, module );
174 }
175
176 projMaker->_generate_proj_file ( module );
177
178 slnMaker._add_project(*projMaker, module);
179
180 delete projMaker;
181 }
182
183 }
184
185 static bool FileExists(string &filename)
186 {
187 ifstream file(filename.c_str());
188
189 if(!file.is_open())
190 return false;
191
192 file.close();
193 return true;
194 }
195
196 void MSVCBackend::ProcessFile(string &filepath)
197 {
198 // Remove the .\ at the start of the filenames
199 if ( filepath[0] == '.' && strchr ( "/\\", filepath[1] ) )
200 filepath.erase(0, 2);
201
202 if(!FileExists(filepath))
203 return;
204
205 // Change the \ to /
206 for(size_t i = 0; i < filepath.length(); i++)
207 {
208 if(filepath[i] == '\\')
209 filepath[i] = '/';
210 }
211
212 // Remove the filename from the path
213 string folder = "";
214
215 size_t pos = filepath.rfind(string("/"), filepath.length() - 1);
216
217 if(pos != string::npos)
218 {
219 folder = filepath;
220 folder.erase(pos, folder.length() - pos);
221 }
222
223 FileUnit fileUnit;
224 fileUnit.filename = filepath;
225 fileUnit.folder = folder;
226
227 m_fileUnits.push_back(fileUnit);
228
229 if(folder != "")
230 AddFolders(folder);
231
232 m_unitCount++;
233 }
234
235 bool MSVCBackend::CheckFolderAdded(string &folder)
236 {
237 for(size_t i = 0; i < m_folders.size(); i++)
238 {
239 if(m_folders[i] == folder)
240 return true;
241 }
242
243 return false;
244 }
245
246 void MSVCBackend::AddFolders(string &folder)
247 {
248 // Check if this folder was already added. true if it was, false otherwise.
249 if(CheckFolderAdded(folder))
250 return;
251
252 m_folders.push_back(folder);
253
254 size_t pos = folder.rfind(string("/"), folder.length() - 1);
255
256 if(pos == string::npos)
257 return;
258
259 folder.erase(pos, folder.length() - pos);
260 AddFolders(folder);
261 }
262
263 void MSVCBackend::OutputFolders()
264 {
265 #if 0
266 m_devFile << "Folders=";
267
268 for(size_t i = 0; i < m_folders.size(); i++)
269 {
270 if(i > 0)
271 m_devFile << ",";
272
273 m_devFile << m_folders[i];
274 }
275 #endif
276 }
277
278 std::string
279 MSVCBackend::UserFileName ( const Module& module, std::string vcproj_file ) const
280 {
281 string computername;
282 string username;
283
284 if (getenv ( "USERNAME" ) != NULL)
285 username = getenv ( "USERNAME" );
286 if (getenv ( "COMPUTERNAME" ) != NULL)
287 computername = getenv ( "COMPUTERNAME" );
288 else if (getenv ( "HOSTNAME" ) != NULL)
289 computername = getenv ( "HOSTNAME" );
290
291 if ((computername != "") && (username != ""))
292 return vcproj_file + "." + computername + "." + username + ".user";
293 else
294 return "";
295 }
296
297 std::string
298 MSVCBackend::SuoFileName ( const Module& module ) const
299 {
300 return FixSeparatorForSystemCommand(
301 ReplaceExtension ( module.output->relative_path + "\\" + module.output->name, "_" + _get_vc_dir() + "_auto.suo" )
302 );
303 }
304
305 std::string
306 MSVCBackend::SlnFileName ( const Module& module ) const
307 {
308 return FixSeparatorForSystemCommand(
309 ReplaceExtension ( module.output->relative_path + "\\" + module.output->name, "_" + _get_vc_dir() + "_auto.sln" )
310 );
311 }
312
313 std::string
314 MSVCBackend::NcbFileName ( const Module& module ) const
315 {
316 return FixSeparatorForSystemCommand(
317 ReplaceExtension ( module.output->relative_path + "\\" + module.output->name, "_" + _get_vc_dir() + "_auto.ncb" )
318 );
319 }
320
321 std::string
322 MSVCBackend::VcprojFileName ( const Module& module ) const
323 {
324 return FixSeparatorForSystemCommand(
325 ReplaceExtension ( module.output->relative_path + "\\" + module.name, "_" + _get_vc_dir() + "_auto.vcproj" )
326 );
327 }
328
329 std::string
330 MSVCBackend::VcxprojFileName ( const Module& module ) const
331 {
332 return FixSeparatorForSystemCommand(
333 ReplaceExtension ( module.output->relative_path + "\\" + module.name, "_" + _get_vc_dir() + "_auto.vcxproj" )
334 );
335 }
336
337 std::string MSVCBackend::_get_vc_dir ( void ) const
338 {
339 if ( configuration.VSProjectVersion == "8.00" )
340 return "vc8";
341 else if ( configuration.VSProjectVersion == "10.00" )
342 return "vc10";
343 else /* default to VS2008 */
344 return "vc9";
345 }
346
347 void
348 MSVCBackend::_get_object_files ( const Module& module, vector<string>& out) const
349 {
350 string basepath = module.output->relative_path;
351 string vcdir = _get_vc_dir ();
352 size_t i;
353 string intenv = Environment::GetIntermediatePath () + DEF_SSEP + basepath + DEF_SSEP;
354 string outenv = Environment::GetOutputPath () + DEF_SSEP + basepath + DEF_SSEP;
355
356 if ( configuration.UseVSVersionInPath )
357 {
358 intenv += vcdir + DEF_SSEP;
359 outenv += vcdir + DEF_SSEP;
360 }
361
362 string dbg = vcdir.substr ( 0, 3 );
363
364 vector<string> cfgs;
365
366 if ( configuration.UseConfigurationInPath )
367 {
368 cfgs.push_back ( intenv + "Debug" );
369 cfgs.push_back ( intenv + "Release" );
370 cfgs.push_back ( intenv + "Speed" );
371 cfgs.push_back ( outenv + "Debug" );
372 cfgs.push_back ( outenv + "Release" );
373 cfgs.push_back ( outenv + "Speed" );
374 }
375 else
376 {
377 cfgs.push_back ( intenv );
378 cfgs.push_back ( outenv );
379 }
380
381 vector<const IfableData*> ifs_list;
382 ifs_list.push_back ( &module.project.non_if_data );
383 ifs_list.push_back ( &module.non_if_data );
384 while ( ifs_list.size () )
385 {
386 const IfableData& data = *ifs_list.back();
387 ifs_list.pop_back();
388 const vector<File*>& files = data.files;
389 for ( i = 0; i < files.size (); i++ )
390 {
391 string file = files[i]->file.relative_path + sSep + files[i]->file.name;
392 string::size_type pos = file.find_last_of (DEF_SSEP);
393 if ( pos != string::npos )
394 file.erase ( 0, pos+1 );
395 if ( !stricmp ( Right(file,3).c_str(), ".rc" ) )
396 file = ReplaceExtension ( file, ".res" );
397 else
398 file = ReplaceExtension ( file, ".obj" );
399 for ( size_t j = 0; j < cfgs.size () / 2; j++ )
400 out.push_back ( cfgs[j] + file );
401 }
402
403 }
404 //common files in intermediate dir
405 for ( i = 0; i < cfgs.size () / 2; i++)
406 {
407 out.push_back ( cfgs[i] + "BuildLog.htm" );
408 out.push_back ( cfgs[i] + dbg + "0.pdb" );
409 out.push_back ( cfgs[i] + dbg + "0.idb" );
410 out.push_back ( cfgs[i] + module.name + ".pch" );
411 }
412 //files in the output dir
413 for ( i = cfgs.size () / 2; i < cfgs.size (); i++ )
414 {
415 out.push_back ( cfgs[i] + module.output->name );
416 out.push_back ( cfgs[i] + module.name + ".pdb" );
417 out.push_back ( cfgs[i] + module.name + ".lib" );
418 out.push_back ( cfgs[i] + module.name + ".exp" );
419 out.push_back ( cfgs[i] + module.name + ".ilk" );
420 }
421 }
422
423 void
424 MSVCBackend::_get_def_files ( const Module& module, vector<string>& out) const
425 {
426 if (module.HasImportLibrary())
427 {
428 #if 0
429 string modulename = module.GetBasePath ();
430 string file = module.importLibrary->definition;
431 size_t pos = file.find (".def");
432 if (pos != string::npos)
433 {
434 file.insert (pos, "_msvc");
435 }
436 modulename += DEF_SSEP + file;
437 out.push_back (modulename);
438 #endif
439 }
440 }
441
442 void
443 MSVCBackend::_clean_project_files ( void )
444 {
445 for( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin(); p != ProjectNode.modules.end(); ++ p )
446 {
447 Module& module = *p->second;
448 vector<string> out;
449 printf("Cleaning project %s %s %s\n", module.name.c_str (), module.output->relative_path.c_str (), NcbFileName ( module ).c_str () );
450
451 string vcproj_file_user = UserFileName(module, VcprojFileName ( module ));
452 if(vcproj_file_user != "")
453 remove ( vcproj_file_user.c_str () );
454
455 _get_object_files ( module, out );
456 _get_def_files ( module, out );
457 for ( size_t j = 0; j < out.size (); j++)
458 {
459 printf("Cleaning file %s\n", out[j].c_str () );
460 remove ( out[j].c_str () );
461 }
462 }
463
464 string filename_sln = ProjectNode.name + ".sln";
465
466 remove ( filename_sln.c_str () );
467 }
468
469 bool
470 MSVCBackend::_copy_file ( const std::string& inputname, const std::string& targetname ) const
471 {
472 FILE * input = fopen ( inputname.c_str (), "rb" );
473 if ( !input )
474 return false;
475
476 FILE * output = fopen ( targetname.c_str (), "wb+" );
477 if ( !output )
478 {
479 fclose ( input );
480 return false;
481 }
482
483 char buffer[256];
484 int num_read;
485 while ( (num_read = fread( buffer, sizeof(char), 256, input) ) || !feof( input ) )
486 fwrite( buffer, sizeof(char), num_read, output );
487
488 fclose ( input );
489 fclose ( output );
490 return true;
491 }
492
493 void
494 MSVCBackend::_install_files (const std::string& vcdir, const::string& config)
495 {
496 for( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin(); p != ProjectNode.modules.end(); ++ p )
497 {
498 Module& module = *p->second;
499 if ( !module.install )
500 continue;
501
502 string inputname = Environment::GetOutputPath () + DEF_SSEP + module.output->relative_path + DEF_SSEP + vcdir + DEF_SSEP + config + DEF_SSEP + module.output->name;
503 string installdir = Environment::GetInstallPath () + DEF_SSEP + module.install->relative_path + DEF_SSEP + module.install->name;
504 if ( _copy_file( inputname, installdir ) )
505 printf ("Installed File :'%s'\n",installdir.c_str () );
506 }
507 }
508
509 std::string
510 MSVCBackend::_get_solution_version ( void )
511 {
512 string version;
513
514 if (configuration.VSProjectVersion.empty())
515 configuration.VSProjectVersion = MS_VS_DEF_VERSION;
516
517 else if (configuration.VSProjectVersion == "8.00")
518 version = "9.00";
519
520 else if (configuration.VSProjectVersion == "9.00")
521 version = "10.00";
522
523 else if (configuration.VSProjectVersion == "10.00")
524 version = "11.00";
525
526 return version;
527 }
528
529 std::string
530 MSVCBackend::_get_studio_version ( void )
531 {
532 string version;
533
534 if (configuration.VSProjectVersion.empty())
535 configuration.VSProjectVersion = MS_VS_DEF_VERSION;
536
537 else if (configuration.VSProjectVersion == "8.00")
538 version = "2005";
539
540 else if (configuration.VSProjectVersion == "9.00")
541 version = "2008";
542
543 else if (configuration.VSProjectVersion == "10.00")
544 version = "2010";
545
546 return version;
547 }
548
549 const Property*
550 MSVCBackend::_lookup_property ( const Module& module, const std::string& name ) const
551 {
552 std::map<std::string, Property*>::const_iterator p;
553
554 /* Check local values */
555 p = module.non_if_data.properties.find(name);
556
557 if ( p != module.non_if_data.properties.end() )
558 return p->second;
559
560 // TODO FIXME - should we check local if-ed properties?
561 p = module.project.non_if_data.properties.find(name);
562
563 if ( p != module.project.non_if_data.properties.end() )
564 return p->second;
565
566 // TODO FIXME - should we check global if-ed properties?
567 return NULL;
568 }