Fix typo in vcproj generator code
[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 // TODO FIXME wine hack?
87 bool only_msvc_headers = false;
88
89 while ( m_configurations.size () > 0 )
90 {
91 const MSVCConfiguration* cfg = m_configurations.back();
92 m_configurations.pop_back();
93 delete cfg;
94 }
95
96 m_configurations.push_back ( new MSVCConfiguration( Debug ));
97 m_configurations.push_back ( new MSVCConfiguration( Release ));
98 m_configurations.push_back ( new MSVCConfiguration( Speed ));
99 m_configurations.push_back ( new MSVCConfiguration( RosBuild ));
100
101 if (!only_msvc_headers)
102 {
103 m_configurations.push_back ( new MSVCConfiguration( Debug, ReactOSHeaders ));
104 m_configurations.push_back ( new MSVCConfiguration( Release, ReactOSHeaders ));
105 m_configurations.push_back ( new MSVCConfiguration( Speed, ReactOSHeaders ));
106 }
107
108 if ( configuration.CleanAsYouGo ) {
109 _clean_project_files();
110 return;
111 }
112 if ( configuration.InstallFiles ) {
113 _install_files( _get_vc_dir(), configuration.VSConfigurationType );
114 return;
115 }
116 string filename_sln ( ProjectNode.name );
117
118 filename_sln += "_auto.sln";
119 printf ( "Creating MSVC workspace: %s\n", filename_sln.c_str() );
120
121 // Write out the project files
122 ProcessModules();
123
124 // Write the solution file
125 SlnMaker slnMaker( configuration, ProjectNode, m_configurations, filename_sln );
126 slnMaker._generate_sln ( _get_solution_version(), _get_studio_version() );
127
128 printf ( "Done.\n" );
129 }
130
131 void MSVCBackend::ProcessModules()
132 {
133 for(std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin(); p != ProjectNode.modules.end(); ++ p)
134 {
135 Module &module = *p->second;
136
137 module.guid = _gen_guid();
138
139 ProjMaker *projMaker;
140
141 if (configuration.VSProjectVersion == "10.00")
142 {
143 string vcxproj_file = VcxprojFileName(module);
144 projMaker = new VCXProjMaker( configuration, m_configurations, vcxproj_file );
145 }
146 else
147 {
148 string vcproj_file = VcprojFileName(module);
149 projMaker = new VCProjMaker( configuration, m_configurations, vcproj_file );
150 }
151
152 projMaker->_generate_proj_file ( module );
153 delete projMaker;
154 }
155 }
156
157 static bool FileExists(string &filename)
158 {
159 ifstream file(filename.c_str());
160
161 if(!file.is_open())
162 return false;
163
164 file.close();
165 return true;
166 }
167
168 void MSVCBackend::ProcessFile(string &filepath)
169 {
170 // Remove the .\ at the start of the filenames
171 if ( filepath[0] == '.' && strchr ( "/\\", filepath[1] ) )
172 filepath.erase(0, 2);
173
174 if(!FileExists(filepath))
175 return;
176
177 // Change the \ to /
178 for(size_t i = 0; i < filepath.length(); i++)
179 {
180 if(filepath[i] == '\\')
181 filepath[i] = '/';
182 }
183
184 // Remove the filename from the path
185 string folder = "";
186
187 size_t pos = filepath.rfind(string("/"), filepath.length() - 1);
188
189 if(pos != string::npos)
190 {
191 folder = filepath;
192 folder.erase(pos, folder.length() - pos);
193 }
194
195 FileUnit fileUnit;
196 fileUnit.filename = filepath;
197 fileUnit.folder = folder;
198
199 m_fileUnits.push_back(fileUnit);
200
201 if(folder != "")
202 AddFolders(folder);
203
204 m_unitCount++;
205 }
206
207 bool MSVCBackend::CheckFolderAdded(string &folder)
208 {
209 for(size_t i = 0; i < m_folders.size(); i++)
210 {
211 if(m_folders[i] == folder)
212 return true;
213 }
214
215 return false;
216 }
217
218 void MSVCBackend::AddFolders(string &folder)
219 {
220 // Check if this folder was already added. true if it was, false otherwise.
221 if(CheckFolderAdded(folder))
222 return;
223
224 m_folders.push_back(folder);
225
226 size_t pos = folder.rfind(string("/"), folder.length() - 1);
227
228 if(pos == string::npos)
229 return;
230
231 folder.erase(pos, folder.length() - pos);
232 AddFolders(folder);
233 }
234
235 void MSVCBackend::OutputFolders()
236 {
237 #if 0
238 m_devFile << "Folders=";
239
240 for(size_t i = 0; i < m_folders.size(); i++)
241 {
242 if(i > 0)
243 m_devFile << ",";
244
245 m_devFile << m_folders[i];
246 }
247 #endif
248 }
249
250 std::string
251 MSVCBackend::SuoFileName ( const Module& module ) const
252 {
253 return FixSeparatorForSystemCommand(
254 ReplaceExtension ( module.output->relative_path + "\\" + module.output->name, "_" + _get_vc_dir() + "_auto.suo" )
255 );
256 }
257
258 std::string
259 MSVCBackend::SlnFileName ( const Module& module ) const
260 {
261 return FixSeparatorForSystemCommand(
262 ReplaceExtension ( module.output->relative_path + "\\" + module.output->name, "_" + _get_vc_dir() + "_auto.sln" )
263 );
264 }
265
266 std::string
267 MSVCBackend::NcbFileName ( const Module& module ) const
268 {
269 return FixSeparatorForSystemCommand(
270 ReplaceExtension ( module.output->relative_path + "\\" + module.output->name, "_" + _get_vc_dir() + "_auto.ncb" )
271 );
272 }
273
274 std::string
275 MSVCBackend::VcprojFileName ( const Module& module ) const
276 {
277 return FixSeparatorForSystemCommand(
278 ReplaceExtension ( module.output->relative_path + "\\" + module.name, "_" + _get_vc_dir() + "_auto.vcproj" )
279 );
280 }
281
282 std::string
283 MSVCBackend::VcxprojFileName ( const Module& module ) const
284 {
285 return FixSeparatorForSystemCommand(
286 ReplaceExtension ( module.output->relative_path + "\\" + module.name, "_" + _get_vc_dir() + "_auto.vcxproj" )
287 );
288 }
289
290 std::string MSVCBackend::_get_vc_dir ( void ) const
291 {
292 if ( configuration.VSProjectVersion == "8.00" )
293 return "vc8";
294 else if ( configuration.VSProjectVersion == "10.00" )
295 return "vc10";
296 else /* default to VS2008 */
297 return "vc9";
298 }
299
300 void
301 MSVCBackend::_get_object_files ( const Module& module, vector<string>& out) const
302 {
303 string basepath = module.output->relative_path;
304 string vcdir = _get_vc_dir ();
305 size_t i;
306 string intenv = Environment::GetIntermediatePath () + DEF_SSEP + basepath + DEF_SSEP;
307 string outenv = Environment::GetOutputPath () + DEF_SSEP + basepath + DEF_SSEP;
308
309 if ( configuration.UseVSVersionInPath )
310 {
311 intenv += vcdir + DEF_SSEP;
312 outenv += vcdir + DEF_SSEP;
313 }
314
315 string dbg = vcdir.substr ( 0, 3 );
316
317 vector<string> cfgs;
318
319 if ( configuration.UseConfigurationInPath )
320 {
321 cfgs.push_back ( intenv + "Debug" );
322 cfgs.push_back ( intenv + "Release" );
323 cfgs.push_back ( intenv + "Speed" );
324 cfgs.push_back ( outenv + "Debug" );
325 cfgs.push_back ( outenv + "Release" );
326 cfgs.push_back ( outenv + "Speed" );
327 }
328 else
329 {
330 cfgs.push_back ( intenv );
331 cfgs.push_back ( outenv );
332 }
333
334 vector<const IfableData*> ifs_list;
335 ifs_list.push_back ( &module.project.non_if_data );
336 ifs_list.push_back ( &module.non_if_data );
337 while ( ifs_list.size () )
338 {
339 const IfableData& data = *ifs_list.back();
340 ifs_list.pop_back();
341 const vector<File*>& files = data.files;
342 for ( i = 0; i < files.size (); i++ )
343 {
344 string file = files[i]->file.relative_path + sSep + files[i]->file.name;
345 string::size_type pos = file.find_last_of (DEF_SSEP);
346 if ( pos != string::npos )
347 file.erase ( 0, pos+1 );
348 if ( !stricmp ( Right(file,3).c_str(), ".rc" ) )
349 file = ReplaceExtension ( file, ".res" );
350 else
351 file = ReplaceExtension ( file, ".obj" );
352 for ( size_t j = 0; j < cfgs.size () / 2; j++ )
353 out.push_back ( cfgs[j] + file );
354 }
355
356 }
357 //common files in intermediate dir
358 for ( i = 0; i < cfgs.size () / 2; i++)
359 {
360 out.push_back ( cfgs[i] + "BuildLog.htm" );
361 out.push_back ( cfgs[i] + dbg + "0.pdb" );
362 out.push_back ( cfgs[i] + dbg + "0.idb" );
363 out.push_back ( cfgs[i] + module.name + ".pch" );
364 }
365 //files in the output dir
366 for ( i = cfgs.size () / 2; i < cfgs.size (); i++ )
367 {
368 out.push_back ( cfgs[i] + module.output->name );
369 out.push_back ( cfgs[i] + module.name + ".pdb" );
370 out.push_back ( cfgs[i] + module.name + ".lib" );
371 out.push_back ( cfgs[i] + module.name + ".exp" );
372 out.push_back ( cfgs[i] + module.name + ".ilk" );
373 }
374 }
375
376 void
377 MSVCBackend::_get_def_files ( const Module& module, vector<string>& out) const
378 {
379 if (module.HasImportLibrary ())
380 {
381 #if 0
382 string modulename = module.GetBasePath ();
383 string file = module.importLibrary->definition;
384 size_t pos = file.find (".def");
385 if (pos != string::npos)
386 {
387 file.insert (pos, "_msvc");
388 }
389 modulename += DEF_SSEP + file;
390 out.push_back (modulename);
391 #endif
392 }
393 }
394
395 void
396 MSVCBackend::_clean_project_files ( void )
397 {
398 for( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin(); p != ProjectNode.modules.end(); ++ p )
399 {
400 Module& module = *p->second;
401 vector<string> out;
402 printf("Cleaning project %s %s %s\n", module.name.c_str (), module.output->relative_path.c_str (), NcbFileName ( module ).c_str () );
403
404 string basepath = module.output->relative_path;
405 remove ( NcbFileName ( module ).c_str () );
406 remove ( SlnFileName ( module ).c_str () );
407 remove ( SuoFileName ( module ).c_str () );
408 remove ( VcprojFileName ( module ).c_str () );
409
410 string username = getenv ( "USERNAME" );
411 string computername = getenv ( "COMPUTERNAME" );
412 string vcproj_file_user = "";
413 #if 0
414 if ((computername != "") && (username != ""))
415 vcproj_file_user = VcprojFileName ( module ) + "." + computername + "." + username + ".user";
416
417 remove ( vcproj_file_user.c_str () );
418 #endif
419 _get_object_files ( module, out );
420 _get_def_files ( module, out );
421 for ( size_t j = 0; j < out.size (); j++)
422 {
423 printf("Cleaning file %s\n", out[j].c_str () );
424 remove ( out[j].c_str () );
425 }
426 }
427
428 string filename_sln = ProjectNode.name + ".sln";
429
430 remove ( filename_sln.c_str () );
431 }
432
433 bool
434 MSVCBackend::_copy_file ( const std::string& inputname, const std::string& targetname ) const
435 {
436 FILE * input = fopen ( inputname.c_str (), "rb" );
437 if ( !input )
438 return false;
439
440 FILE * output = fopen ( targetname.c_str (), "wb+" );
441 if ( !output )
442 {
443 fclose ( input );
444 return false;
445 }
446
447 char buffer[256];
448 int num_read;
449 while ( (num_read = fread( buffer, sizeof(char), 256, input) ) || !feof( input ) )
450 fwrite( buffer, sizeof(char), num_read, output );
451
452 fclose ( input );
453 fclose ( output );
454 return true;
455 }
456
457 void
458 MSVCBackend::_install_files (const std::string& vcdir, const::string& config)
459 {
460 for( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin(); p != ProjectNode.modules.end(); ++ p )
461 {
462 Module& module = *p->second;
463 if ( !module.install )
464 continue;
465
466 string inputname = Environment::GetOutputPath () + DEF_SSEP + module.output->relative_path + DEF_SSEP + vcdir + DEF_SSEP + config + DEF_SSEP + module.output->name;
467 string installdir = Environment::GetInstallPath () + DEF_SSEP + module.install->relative_path + DEF_SSEP + module.install->name;
468 if ( _copy_file( inputname, installdir ) )
469 printf ("Installed File :'%s'\n",installdir.c_str () );
470 }
471 }
472
473 std::string
474 MSVCBackend::_get_solution_version ( void )
475 {
476 string version;
477
478 if (configuration.VSProjectVersion.empty())
479 configuration.VSProjectVersion = MS_VS_DEF_VERSION;
480
481 else if (configuration.VSProjectVersion == "8.00")
482 version = "9.00";
483
484 else if (configuration.VSProjectVersion == "9.00")
485 version = "10.00";
486
487 else if (configuration.VSProjectVersion == "10.00")
488 version = "11.00";
489
490 return version;
491 }
492
493 std::string
494 MSVCBackend::_get_studio_version ( void )
495 {
496 string version;
497
498 if (configuration.VSProjectVersion.empty())
499 configuration.VSProjectVersion = MS_VS_DEF_VERSION;
500
501 else if (configuration.VSProjectVersion == "8.00")
502 version = "2005";
503
504 else if (configuration.VSProjectVersion == "9.00")
505 version = "2008";
506
507 else if (configuration.VSProjectVersion == "10.00")
508 version = "2010";
509
510 return version;
511 }
512
513 const Property*
514 MSVCBackend::_lookup_property ( const Module& module, const std::string& name ) const
515 {
516 std::map<std::string, Property*>::const_iterator p;
517
518 /* Check local values */
519 p = module.non_if_data.properties.find(name);
520
521 if ( p != module.non_if_data.properties.end() )
522 return p->second;
523
524 // TODO FIXME - should we check local if-ed properties?
525 p = module.project.non_if_data.properties.find(name);
526
527 if ( p != module.project.non_if_data.properties.end() )
528 return p->second;
529
530 // TODO FIXME - should we check global if-ed properties?
531 return NULL;
532 }