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