1 | | /* ScummVM - Graphic Adventure Engine |
2 | | * |
3 | | * ScummVM is the legal property of its developers, whose names |
4 | | * are too numerous to list here. Please refer to the COPYRIGHT |
5 | | * file distributed with this source distribution. |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU General Public License |
9 | | * as published by the Free Software Foundation; either version 2 |
10 | | * of the License, or (at your option) any later version. |
11 | | |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
20 | | * |
21 | | * $URL$ |
22 | | * $Id$ |
23 | | * |
24 | | */ |
25 | | |
26 | | #include "create_msvc.h" |
27 | | |
28 | | #include <fstream> |
29 | | #include <iostream> |
30 | | #include <map> |
31 | | #include <stack> |
32 | | #include <algorithm> |
33 | | #include <sstream> |
34 | | #include <iomanip> |
35 | | |
36 | | #include <cassert> |
37 | | #include <cctype> |
38 | | #include <sstream> |
39 | | #include <cstring> |
40 | | #include <cstdlib> |
41 | | #include <ctime> |
42 | | |
43 | | #if defined(_WIN32) || defined(WIN32) |
44 | | #include <windows.h> |
45 | | #else |
46 | | #include <sys/param.h> |
47 | | #include <sys/stat.h> |
48 | | #include <dirent.h> |
49 | | #endif |
50 | | |
51 | | namespace { |
52 | | /** |
53 | | * Converts the given path to only use backslashes. |
54 | | * This means that for example the path: |
55 | | * foo/bar\test.txt |
56 | | * will be converted to: |
57 | | * foo\bar\test.txt |
58 | | * |
59 | | * @param path Path string. |
60 | | * @return Converted path. |
61 | | */ |
62 | | std::string convertPathToWin(const std::string &path); |
63 | | |
64 | | /** |
65 | | * Converts the given path to only use slashes as |
66 | | * delimiters. |
67 | | * This means that for example the path: |
68 | | * foo/bar\test.txt |
69 | | * will be converted to: |
70 | | * foo/bar/test.txt |
71 | | * |
72 | | * @param path Path string. |
73 | | * @return Converted path. |
74 | | */ |
75 | | std::string unifyPath(const std::string &path); |
76 | | |
77 | | /** |
78 | | * Returns the last path component. |
79 | | * |
80 | | * @param path Path string. |
81 | | * @return Last path component. |
82 | | */ |
83 | | std::string getLastPathComponent(const std::string &path); |
84 | | |
85 | | /** |
86 | | * Display the help text for the program. |
87 | | * |
88 | | * @param exe Name of the executable. |
89 | | */ |
90 | | void displayHelp(const char *exe); |
91 | | |
92 | | /** |
93 | | * Structure representing a file tree. This contains two |
94 | | * members: name and children. "name" holds the name of |
95 | | * the node. "children" does contain all the node's children. |
96 | | * When the list "children" is empty, the node is a file entry, |
97 | | * otherwise it's a directory. |
98 | | */ |
99 | | struct FileNode { |
100 | | typedef std::list<FileNode *> NodeList; |
101 | | |
102 | | FileNode(const std::string &n) : name(n), children() {} |
103 | | |
104 | | ~FileNode() { |
105 | | for (NodeList::iterator i = children.begin(); i != children.end(); ++i) |
106 | | delete *i; |
107 | | } |
108 | | |
109 | | std::string name; ///< Name of the node |
110 | | NodeList children; ///< List of children for the node |
111 | | }; |
112 | | |
113 | | /** |
114 | | * Structure for describing an FSNode. This is a very minimalistic |
115 | | * description, which includes everything we need. |
116 | | * It only contains the name of the node and whether it is a directory |
117 | | * or not. |
118 | | */ |
119 | | struct FSNode { |
120 | | FSNode() : name(), isDirectory(false) {} |
121 | | FSNode(const std::string &n, bool iD) : name(n), isDirectory(iD) {} |
122 | | |
123 | | std::string name; ///< Name of the file system node |
124 | | bool isDirectory; ///< Whether it is a directory or not |
125 | | }; |
126 | | |
127 | | typedef std::list<FSNode> FileList; |
128 | | |
129 | | class ProjectProvider { |
130 | | public: |
131 | | typedef std::map<std::string, std::string> UUIDMap; |
132 | | |
133 | | protected: |
134 | | const int _version; ///< Target MSVC version |
135 | | std::string _globalWarnings; ///< Global warnings |
136 | | std::map<std::string, std::string> _projectWarnings; ///< Per-project warnings |
137 | | |
138 | | UUIDMap _uuidMap; ///< List of (project name, UUID) pairs |
139 | | |
140 | | public: |
141 | | /** |
142 | | * Instantiate new ProjectProvider class |
143 | | * |
144 | | * @param version Target MSVC version. |
145 | | */ |
146 | | ProjectProvider(const int version, std::string global_warnings, std::map<std::string, std::string> project_warnings); |
147 | | virtual ~ProjectProvider() {} |
148 | | |
149 | | /** |
150 | | * Creates all MSVC build files: the solution |
151 | | * for all projects, all projects itself and the |
152 | | * global config files. |
153 | | * |
154 | | * @param setup Description of the desired build setup. |
155 | | */ |
156 | | void createMSVCProject(const BuildSetup &setup); |
157 | | |
158 | | /** |
159 | | * Creates the main solution file "scummvm.sln" for a specific |
160 | | * build setup. |
161 | | * |
162 | | * @param setup Description of the desired build. |
163 | | */ |
164 | | void createScummVMSolution(const BuildSetup &setup); |
165 | | |
166 | | /** |
167 | | * Create a project file for the specified list of files. |
168 | | * |
169 | | * @param name Name of the project file. |
170 | | * @param uuid UUID of the project file. |
171 | | * @param setup Description of the desired build. |
172 | | * @param moduleDir Path to the module. |
173 | | * @param includeList Files to include (must have "moduleDir" as prefix). |
174 | | * @param excludeList Files to exclude (must have "moduleDir" as prefix). |
175 | | */ |
176 | | virtual void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir, |
177 | | const StringList &includeList, const StringList &excludeList) = 0; |
178 | | |
179 | | /** |
180 | | * Writes file entries for the specified directory node into |
181 | | * the given project file. It will also take care of duplicate |
182 | | * object files. |
183 | | * |
184 | | * @param dir Directory node. |
185 | | * @param projectFile File stream to write to. |
186 | | * @param indentation Indentation level to use. |
187 | | * @param duplicate List of duplicate object file names. |
188 | | * @param objPrefix Prefix to use for object files, which would name clash. |
189 | | * @param filePrefix Generic prefix to all files of the node. |
190 | | */ |
191 | | virtual void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, |
192 | | const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix) = 0; |
193 | | |
194 | | /** |
195 | | * Output a list of project references to the file stream |
196 | | * |
197 | | * @param output File stream to write to. |
198 | | */ |
199 | | virtual void writeReferences(std::ofstream &output) = 0; |
200 | | |
201 | | /** |
202 | | * Outputs a property file based on the input parameters. |
203 | | * |
204 | | * It can be easily used to create different global properties files |
205 | | * for a 64 bit and a 32 bit version. It will also take care that the |
206 | | * two platform configurations will output their files into different |
207 | | * directories. |
208 | | * |
209 | | * @param properties File stream in which to write the property settings. |
210 | | * @param bits Number of bits the platform supports. |
211 | | * @param defines Defines the platform needs to have set. |
212 | | * @param prefix File prefix, used to add additional include paths. |
213 | | * @param isWin32 Bitness of property file |
214 | | */ |
215 | | virtual void outputGlobalPropFile(std::ofstream &properties, int bits, const std::string &defines, const std::string &prefix, bool isWin32) = 0; |
216 | | |
217 | | /** |
218 | | * Generates the project properties for debug and release settings. |
219 | | * |
220 | | * @param setup Description of the desired build setup. |
221 | | * @param isRelease Type of property file |
222 | | * @param isWin32 Bitness of property file |
223 | | */ |
224 | | virtual void createBuildProp(const BuildSetup &setup, bool isRelease, bool isWin32) = 0; |
225 | | |
226 | | /** |
227 | | * Get the file extension for project files |
228 | | */ |
229 | | virtual const char *getProjectExtension() = 0; |
230 | | |
231 | | /** |
232 | | * Get the file extension for property files |
233 | | */ |
234 | | virtual const char *getPropertiesExtension() = 0; |
235 | | |
236 | | /** |
237 | | * Get the Visual Studio version (used by the VS shell extension to launch the correct VS version) |
238 | | */ |
239 | | virtual int getVisualStudioVersion() = 0; |
240 | | |
241 | | /** |
242 | | * Create the global project properties. |
243 | | * |
244 | | * @param setup Description of the desired build setup. |
245 | | */ |
246 | | void createGlobalProp(const BuildSetup &setup); |
247 | | |
248 | | /** |
249 | | * Adds files of the specified directory recursively to given project file. |
250 | | * |
251 | | * @param dir Path to the directory. |
252 | | * @param projectFile Output stream object, where all data should be written to. |
253 | | * @param includeList Files to include (must have a relative directory as prefix). |
254 | | * @param excludeList Files to exclude (must have a relative directory as prefix). |
255 | | * @param filePrefix Prefix to use for relative path arguments. |
256 | | */ |
257 | | void addFilesToProject(const std::string &dir, std::ofstream &projectFile, |
258 | | const StringList &includeList, const StringList &excludeList, |
259 | | const std::string &filePrefix); |
260 | | |
261 | | /** |
262 | | * Creates a list of files of the specified module. This also |
263 | | * creates a list of files, which should not be included. |
264 | | * All filenames will have "moduleDir" as prefix. |
265 | | * |
266 | | * @param moduleDir Path to the module. |
267 | | * @param defines List of set defines. |
268 | | * @param includeList Reference to a list, where included files should be added. |
269 | | * @param excludeList Reference to a list, where excluded files should be added. |
270 | | */ |
271 | | void createModuleList(const std::string &moduleDir, const StringList &defines, StringList &includeList, StringList &excludeList); |
272 | | }; |
273 | | |
274 | | class VisualStudioProvider : public ProjectProvider { |
275 | | public: |
276 | | VisualStudioProvider(const int version, std::string global_warnings, std::map<std::string, std::string> project_warnings); |
277 | | |
278 | | void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir, |
279 | | const StringList &includeList, const StringList &excludeList); |
280 | | |
281 | | void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, |
282 | | const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix); |
283 | | |
284 | | void writeReferences(std::ofstream &output); |
285 | | |
286 | | void outputGlobalPropFile(std::ofstream &properties, int bits, const std::string &defines, const std::string &prefix, bool isWin32); |
287 | | |
288 | | void createBuildProp(const BuildSetup &setup, bool isRelease, bool isWin32); |
289 | | |
290 | | const char *getProjectExtension(); |
291 | | const char *getPropertiesExtension(); |
292 | | int getVisualStudioVersion(); |
293 | | }; |
294 | | |
295 | | class MSBuildProvider : public ProjectProvider { |
296 | | public: |
297 | | MSBuildProvider(const int version, std::string global_warnings, std::map<std::string, std::string> project_warnings); |
298 | | |
299 | | void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir, |
300 | | const StringList &includeList, const StringList &excludeList); |
301 | | |
302 | | void outputProjectSettings(std::ofstream &project, const std::string &name, const BuildSetup &setup, bool isRelease, bool isWin32); |
303 | | |
304 | | void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, |
305 | | const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix); |
306 | | |
307 | | void writeReferences(std::ofstream &output); |
308 | | |
309 | | void outputGlobalPropFile(std::ofstream &properties, int bits, const std::string &defines, const std::string &prefix, bool isWin32); |
310 | | |
311 | | void createBuildProp(const BuildSetup &setup, bool isRelease, bool isWin32); |
312 | | |
313 | | const char *getProjectExtension(); |
314 | | const char *getPropertiesExtension(); |
315 | | int getVisualStudioVersion(); |
316 | | |
317 | | private: |
318 | | struct FileEntry { |
319 | | std::string name; |
320 | | std::string path; |
321 | | std::string filter; |
322 | | std::string prefix; |
323 | | |
324 | | bool operator<(const FileEntry& rhs) { |
325 | | return path.compare(rhs.path) == -1; // Not exactly right for alphabetical order, but good enough |
326 | | } |
327 | | }; |
328 | | typedef std::list<FileEntry> FileEntries; |
329 | | |
330 | | std::list<std::string> _filters; // list of filters (we need to create a GUID for each filter id) |
331 | | FileEntries _compileFiles; |
332 | | FileEntries _includeFiles; |
333 | | FileEntries _otherFiles; |
334 | | FileEntries _asmFiles; |
335 | | FileEntries _resourceFiles; |
336 | | |
337 | | void computeFileList(const FileNode &dir, const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix); |
338 | | void createFiltersFile(const BuildSetup &setup, const std::string &name); |
339 | | }; |
340 | | |
341 | | } // End of anonymous namespace |
342 | | |
343 | | int main(int argc, char *argv[]) { |
344 | | #if !(defined(_WIN32) || defined(WIN32)) |
345 | | // Initialize random number generator for UUID creation |
346 | | std::srand(std::time(0)); |
347 | | #endif |
348 | | |
349 | | if (argc < 2) { |
350 | | displayHelp(argv[0]); |
351 | | return -1; |
352 | | } |
353 | | |
354 | | const std::string srcDir = argv[1]; |
355 | | |
356 | | BuildSetup setup; |
357 | | setup.srcDir = unifyPath(srcDir); |
358 | | |
359 | | if (setup.srcDir.at(setup.srcDir.size() - 1) == '/') |
360 | | setup.srcDir.erase(setup.srcDir.size() - 1); |
361 | | |
362 | | setup.filePrefix = setup.srcDir; |
363 | | setup.outputDir = '.'; |
364 | | |
365 | | setup.engines = parseConfigure(setup.srcDir); |
366 | | |
367 | | if (setup.engines.empty()) { |
368 | | std::cout << "WARNING: No engines found in configure file or configure file missing in \"" << setup.srcDir << "\"\n"; |
369 | | return 0; |
370 | | } |
371 | | |
372 | | setup.features = getAllFeatures(); |
373 | | |
374 | | int msvcVersion = 9; |
375 | | // Parse command line arguments |
376 | | using std::cout; |
377 | | for (int i = 2; i < argc; ++i) { |
378 | | if (!std::strcmp(argv[i], "--list-engines")) { |
379 | | cout << " The following enables are available in the ScummVM source destribution\n" |
380 | | " located at \"" << srcDir << "\":\n"; |
381 | | |
382 | | cout << " state | name | description\n\n"; |
383 | | cout.setf(std::ios_base::left, std::ios_base::adjustfield); |
384 | | for (EngineDescList::const_iterator j = setup.engines.begin(); j != setup.engines.end(); ++j) |
385 | | cout << ' ' << (j->enable ? " enabled" : "disabled") << " | " << std::setw(15) << j->name << std::setw(0) << " | " << j->desc << "\n"; |
386 | | cout.setf(std::ios_base::right, std::ios_base::adjustfield); |
387 | | |
388 | | return 0; |
389 | | } else if (!std::strcmp(argv[i], "--msvc-version")) { |
390 | | if (i + 1 >= argc) { |
391 | | std::cerr << "ERROR: Missing \"version\" parameter for \"--msvc-version\"!\n"; |
392 | | return -1; |
393 | | } |
394 | | |
395 | | msvcVersion = atoi(argv[++i]); |
396 | | |
397 | | if (msvcVersion != 8 && msvcVersion != 9 && msvcVersion != 10) { |
398 | | std::cerr << "ERROR: Unsupported version: \"" << msvcVersion << "\" passed to \"--msvc-version\"!\n"; |
399 | | return -1; |
400 | | } |
401 | | } else if (!strncmp(argv[i], "--enable-", 9)) { |
402 | | const char *name = &argv[i][9]; |
403 | | if (!*name) { |
404 | | std::cerr << "ERROR: Invalid command \"" << argv[i] << "\"\n"; |
405 | | return -1; |
406 | | } |
407 | | |
408 | | if (!std::strcmp(name, "all-engines")) { |
409 | | for (EngineDescList::iterator j = setup.engines.begin(); j != setup.engines.end(); ++j) |
410 | | j->enable = true; |
411 | | } else if (!setEngineBuildState(name, setup.engines, true)) { |
412 | | // If none found, we'll try the features list |
413 | | if (!setFeatureBuildState(name, setup.features, true)) { |
414 | | std::cerr << "ERROR: \"" << name << "\" is neither an engine nor a feature!\n"; |
415 | | return -1; |
416 | | } |
417 | | } |
418 | | } else if (!strncmp(argv[i], "--disable-", 10)) { |
419 | | const char *name = &argv[i][10]; |
420 | | if (!*name) { |
421 | | std::cerr << "ERROR: Invalid command \"" << argv[i] << "\"\n"; |
422 | | return -1; |
423 | | } |
424 | | |
425 | | if (!std::strcmp(name, "all-engines")) { |
426 | | for (EngineDescList::iterator j = setup.engines.begin(); j != setup.engines.end(); ++j) |
427 | | j->enable = false; |
428 | | } else if (!setEngineBuildState(name, setup.engines, false)) { |
429 | | // If none found, we'll try the features list |
430 | | if (!setFeatureBuildState(name, setup.features, false)) { |
431 | | std::cerr << "ERROR: \"" << name << "\" is neither an engine nor a feature!\n"; |
432 | | return -1; |
433 | | } |
434 | | } |
435 | | } else if (!std::strcmp(argv[i], "--file-prefix")) { |
436 | | if (i + 1 >= argc) { |
437 | | std::cerr << "ERROR: Missing \"prefix\" parameter for \"--file-prefix\"!\n"; |
438 | | return -1; |
439 | | } |
440 | | |
441 | | setup.filePrefix = unifyPath(argv[++i]); |
442 | | if (setup.filePrefix.at(setup.filePrefix.size() - 1) == '/') |
443 | | setup.filePrefix.erase(setup.filePrefix.size() - 1); |
444 | | } else if (!std::strcmp(argv[i], "--output-dir")) { |
445 | | if (i + 1 >= argc) { |
446 | | std::cerr << "ERROR: Missing \"path\" parameter for \"--output-dirx\"!\n"; |
447 | | return -1; |
448 | | } |
449 | | |
450 | | setup.outputDir = unifyPath(argv[++i]); |
451 | | if (setup.outputDir.at(setup.outputDir.size() - 1) == '/') |
452 | | setup.outputDir.erase(setup.outputDir.size() - 1); |
453 | | } else { |
454 | | std::cerr << "ERROR: Unknown parameter \"" << argv[i] << "\"\n"; |
455 | | return -1; |
456 | | } |
457 | | } |
458 | | |
459 | | // Print status |
460 | | cout << "Enabled engines:\n\n"; |
461 | | for (EngineDescList::const_iterator i = setup.engines.begin(); i != setup.engines.end(); ++i) { |
462 | | if (i->enable) |
463 | | cout << " " << i->desc << '\n'; |
464 | | } |
465 | | |
466 | | cout << "\nDisabled engines:\n\n"; |
467 | | for (EngineDescList::const_iterator i = setup.engines.begin(); i != setup.engines.end(); ++i) { |
468 | | if (!i->enable) |
469 | | cout << " " << i->desc << '\n'; |
470 | | } |
471 | | |
472 | | cout << "\nEnabled features:\n\n"; |
473 | | for (FeatureList::const_iterator i = setup.features.begin(); i != setup.features.end(); ++i) { |
474 | | if (i->enable) |
475 | | cout << " " << i->description << '\n'; |
476 | | } |
477 | | |
478 | | cout << "\nDisabled features:\n\n"; |
479 | | for (FeatureList::const_iterator i = setup.features.begin(); i != setup.features.end(); ++i) { |
480 | | if (!i->enable) |
481 | | cout << " " << i->description << '\n'; |
482 | | } |
483 | | |
484 | | // Creation... |
485 | | setup.defines = getEngineDefines(setup.engines); |
486 | | StringList featureDefines = getFeatureDefines(setup.features); |
487 | | setup.defines.splice(setup.defines.begin(), featureDefines); |
488 | | |
489 | | setup.libraries = getFeatureLibraries(setup.features); |
490 | | |
491 | | setup.libraries.push_back("winmm.lib"); |
492 | | setup.libraries.push_back("sdl.lib"); |
493 | | |
494 | | |
495 | | // List of global warnings and a map for project-specific warnings |
496 | | std::string globalWarnings; |
497 | | std::map<std::string, std::string> projectWarnings; |
498 | | |
499 | | //////////////////////////////////////////////////////////////////////////// |
500 | | // Initialize global & project-specific warnings |
501 | | // |
502 | | // Tracker reference: |
503 | | // https://sourceforge.net/tracker/?func=detail&aid=2909981&group_id=37116&atid=418822 |
504 | | // |
505 | | //////////////////////////////////////////////////////////////////////////// |
506 | | // |
507 | | // 4068 (unknown pragma) |
508 | | // only used in scumm engine to mark code sections |
509 | | // |
510 | | // 4100 (unreferenced formal parameter) |
511 | | // |
512 | | // 4103 (alignment changed after including header, may be due to missing #pragma pack(pop)) |
513 | | // used by pack-start / pack-end |
514 | | // |
515 | | // 4127 (conditional expression is constant) |
516 | | // used in a lot of engines |
517 | | // |
518 | | // 4244 ('conversion' conversion from 'type1' to 'type2', possible loss of data) |
519 | | // throws tons and tons of warnings, most of them false positives |
520 | | // |
521 | | // 4250 ('class1' : inherits 'class2::member' via dominance) |
522 | | // two or more members have the same name. Should be harmless |
523 | | // |
524 | | // 4310 (cast truncates constant value) |
525 | | // used in some engines |
526 | | // |
527 | | // 4351 (new behavior: elements of array 'array' will be default initialized) |
528 | | // a change in behavior in Visual Studio 2005. We want the new behavior, so it can be disabled |
529 | | // |
530 | | // 4512 ('class' : assignment operator could not be generated) |
531 | | // some classes use const items and the default assignment operator cannot be generated |
532 | | // |
533 | | // 4702 (unreachable code) |
534 | | // mostly thrown after error() calls (marked as NORETURN) |
535 | | // |
536 | | // 4706 (assignment within conditional expression) |
537 | | // used in a lot of engines |
538 | | // |
539 | | // 4800 ('type' : forcing value to bool 'true' or 'false' (performance warning)) |
540 | | // |
541 | | // 4996 ('function': was declared deprecated) |
542 | | // disabling it removes all the non-standard unsafe functions warnings (strcpy_s, etc.) |
543 | | // |
544 | | //////////////////////////////////////////////////////////////////////////// |
545 | | // |
546 | | // 4189 (local variable is initialized but not referenced) |
547 | | // false positive in lure engine |
548 | | // |
549 | | // 4355 ('this' : used in base member initializer list) |
550 | | // only disabled for specific engines where it is used in a safe way |
551 | | // |
552 | | // 4510 ('class' : default constructor could not be generated) |
553 | | // |
554 | | // 4511 ('class' : copy constructor could not be generated) |
555 | | // |
556 | | // 4610 (object 'class' can never be instantiated - user-defined constructor required) |
557 | | // "correct" but harmless (as is 4510) |
558 | | // |
559 | | //////////////////////////////////////////////////////////////////////////// |
560 | | |
561 | | globalWarnings = "4068;4100;4103;4127;4244;4250;4310;4351;4512;4702;4706;4800;4996"; |
562 | | |
563 | | projectWarnings["agi"] = "4510;4610"; |
564 | | projectWarnings["agos"] = "4511"; |
565 | | projectWarnings["lure"] = "4189;4355"; |
566 | | projectWarnings["kyra"] = "4355"; |
567 | | projectWarnings["m4"] = "4355"; |
568 | | |
569 | | ProjectProvider *provider = NULL; |
570 | | |
571 | | if (msvcVersion == 8 || msvcVersion == 9) |
572 | | provider = new VisualStudioProvider(msvcVersion, globalWarnings, projectWarnings); |
573 | | else |
574 | | provider = new MSBuildProvider(msvcVersion, globalWarnings, projectWarnings); |
575 | | |
576 | | provider->createMSVCProject(setup); |
577 | | |
578 | | delete provider; |
579 | | } |
580 | | |
581 | | namespace { |
582 | | std::string convertPathToWin(const std::string &path) { |
583 | | std::string result = path; |
584 | | std::replace(result.begin(), result.end(), '/', '\\'); |
585 | | return result; |
586 | | } |
587 | | |
588 | | std::string unifyPath(const std::string &path) { |
589 | | std::string result = path; |
590 | | std::replace(result.begin(), result.end(), '\\', '/'); |
591 | | return result; |
592 | | } |
593 | | |
594 | | std::string getLastPathComponent(const std::string &path) { |
595 | | std::string::size_type pos = path.find_last_of('/'); |
596 | | if (pos == std::string::npos) |
597 | | return path; |
598 | | else |
599 | | return path.substr(pos + 1); |
600 | | } |
601 | | |
602 | | void displayHelp(const char *exe) { |
603 | | using std::cout; |
604 | | |
605 | | cout << "Usage:\n" |
606 | | << exe << " path\\to\\source [optional options]\n" |
607 | | << "\n" |
608 | | << " Creates MSVC project files for the ScummVM source locatd at \"path\\to\\source\".\n" |
609 | | " The project files will be created in the directory where tool is run from and\n" |
610 | | " will include \"path\\to\\source\" for relative file paths, thus be sure that you\n" |
611 | | " pass a relative file path like \"..\\..\\trunk\".\n" |
612 | | "\n" |
613 | | " Additionally there are the following switches for changing various settings:\n" |
614 | | "\n" |
615 | | "MSVC specifc settings:\n" |
616 | | " --msvc-version version set the targeted MSVC version. Possible values:\n" |
617 | | " 8 stands for \"Visual Studio 2005\"\n" |
618 | | " 9 stands for \"Visual Studio 2008\"\n" |
619 | | " 10 stands for \"Visual Studio 2010\" (Experimental)\n" |
620 | | " The default is \"9\", thus \"Visual Studio 2008\"\n" |
621 | | " --file-prefix prefix allow overwriting of relative file prefix in the\n" |
622 | | " MSVC project files. By default the prefix is the\n" |
623 | | " \"path\\to\\source\" argument\n" |
624 | | " --output-dir path overwrite path, where the project files are placed\n" |
625 | | " By default this is \".\", i.e. the current working\n" |
626 | | " directory\n" |
627 | | "\n" |
628 | | "ScummVM engine settings:\n" |
629 | | " --list-engines list all available engines and their default state\n" |
630 | | " --enable-engine enable building of the engine with the name \"engine\"\n" |
631 | | " --disable-engine disable building of the engine with the name \"engine\"\n" |
632 | | " --enable-all-engines enable building of all engines\n" |
633 | | " --disable-all-engines disable building of all engines\n" |
634 | | "\n" |
635 | | "ScummVM optional feature settings:\n" |
636 | | " --enable-name enable inclusion of the feature \"name\"\n" |
637 | | " --disable-name disable inclusion of the feature \"name\"\n" |
638 | | "\n" |
639 | | " There are the following features available:\n" |
640 | | "\n"; |
641 | | |
642 | | cout << " state | name | description\n\n"; |
643 | | const FeatureList features = getAllFeatures(); |
644 | | cout.setf(std::ios_base::left, std::ios_base::adjustfield); |
645 | | for (FeatureList::const_iterator i = features.begin(); i != features.end(); ++i) |
646 | | cout << ' ' << (i->enable ? " enabled" : "disabled") << " | " << std::setw(15) << i->name << std::setw(0) << " | " << i->description << '\n'; |
647 | | cout.setf(std::ios_base::right, std::ios_base::adjustfield); |
648 | | } |
649 | | |
650 | | typedef StringList TokenList; |
651 | | |
652 | | /** |
653 | | * Takes a given input line and creates a list of tokens out of it. |
654 | | * |
655 | | * A token in this context is seperated by whitespaces. A special case |
656 | | * are quotation marks though. A string inside quotation marks is treated |
657 | | * as single token, even when it contains whitespaces. |
658 | | * |
659 | | * Thus for example the input: |
660 | | * foo bar "1 2 3 4" ScummVM |
661 | | * will create a list with the following entries: |
662 | | * "foo", "bar", "1 2 3 4", "ScummVM" |
663 | | * As you can see the quotation marks will get *removed* too. |
664 | | * |
665 | | * @param input The text to be tokenized. |
666 | | * @return A list of tokens. |
667 | | */ |
668 | | TokenList tokenize(const std::string &input); |
669 | | |
670 | | /** |
671 | | * Try to parse a given line and create an engine definition |
672 | | * out of the result. |
673 | | * |
674 | | * This may take *any* input line, when the line is not used |
675 | | * to define an engine the result of the function will be "false". |
676 | | * |
677 | | * Note that the contents of "engine" are undefined, when this |
678 | | * function returns "false". |
679 | | * |
680 | | * @param line Text input line. |
681 | | * @param engine Reference to an object, where the engine information |
682 | | * is to be stored in. |
683 | | * @return "true", when parsing succeeded, "false" otherwise. |
684 | | */ |
685 | | bool parseEngine(const std::string &line, EngineDesc &engine); |
686 | | } // End of anonymous namespace |
687 | | |
688 | | EngineDescList parseConfigure(const std::string &srcDir) { |
689 | | std::string configureFile = srcDir + "/configure"; |
690 | | |
691 | | std::ifstream configure(configureFile.c_str()); |
692 | | if (!configure) |
693 | | return EngineDescList(); |
694 | | |
695 | | std::string line; |
696 | | EngineDescList engines; |
697 | | |
698 | | while (true) { |
699 | | std::getline(configure, line); |
700 | | if (configure.eof()) |
701 | | break; |
702 | | |
703 | | if (configure.fail()) |
704 | | error("Failed while reading from " + configureFile); |
705 | | |
706 | | EngineDesc desc; |
707 | | if (parseEngine(line, desc)) |
708 | | engines.push_back(desc); |
709 | | } |
710 | | |
711 | | return engines; |
712 | | } |
713 | | |
714 | | bool isSubEngine(const std::string &name, const EngineDescList &engines) { |
715 | | for (EngineDescList::const_iterator i = engines.begin(); i != engines.end(); ++i) { |
716 | | if (std::find(i->subEngines.begin(), i->subEngines.end(), name) != i->subEngines.end()) |
717 | | return true; |
718 | | } |
719 | | |
720 | | return false; |
721 | | } |
722 | | |
723 | | bool setEngineBuildState(const std::string &name, EngineDescList &engines, bool enable) { |
724 | | if (enable && isSubEngine(name, engines)) { |
725 | | // When we enable a sub engine, we need to assure that the parent is also enabled, |
726 | | // thus we enable both sub engine and parent over here. |
727 | | EngineDescList::iterator engine = std::find(engines.begin(), engines.end(), name); |
728 | | if (engine != engines.end()) { |
729 | | engine->enable = enable; |
730 | | |
731 | | for (engine = engines.begin(); engine != engines.end(); ++engine) { |
732 | | if (std::find(engine->subEngines.begin(), engine->subEngines.end(), name) != engine->subEngines.end()) { |
733 | | engine->enable = true; |
734 | | break; |
735 | | } |
736 | | } |
737 | | |
738 | | return true; |
739 | | } |
740 | | } else { |
741 | | EngineDescList::iterator engine = std::find(engines.begin(), engines.end(), name); |
742 | | if (engine != engines.end()) { |
743 | | engine->enable = enable; |
744 | | |
745 | | // When we disable an einge, we also need to disable all the sub engines. |
746 | | if (!enable && !engine->subEngines.empty()) { |
747 | | for (StringList::const_iterator j = engine->subEngines.begin(); j != engine->subEngines.end(); ++j) { |
748 | | EngineDescList::iterator subEngine = std::find(engines.begin(), engines.end(), *j); |
749 | | if (subEngine != engines.end()) |
750 | | subEngine->enable = false; |
751 | | } |
752 | | } |
753 | | |
754 | | return true; |
755 | | } |
756 | | } |
757 | | |
758 | | return false; |
759 | | } |
760 | | |
761 | | StringList getEngineDefines(const EngineDescList &engines) { |
762 | | StringList result; |
763 | | |
764 | | for (EngineDescList::const_iterator i = engines.begin(); i != engines.end(); ++i) { |
765 | | if (i->enable) { |
766 | | std::string define = "ENABLE_" + i->name; |
767 | | std::transform(define.begin(), define.end(), define.begin(), toupper); |
768 | | result.push_back(define); |
769 | | } |
770 | | } |
771 | | |
772 | | return result; |
773 | | } |
774 | | |
775 | | namespace { |
776 | | bool parseEngine(const std::string &line, EngineDesc &engine) { |
777 | | // Format: |
778 | | // add_engine engine_name "Readable Description" enable_default ["SubEngineList"] |
779 | | TokenList tokens = tokenize(line); |
780 | | |
781 | | if (tokens.size() < 4) |
782 | | return false; |
783 | | |
784 | | TokenList::const_iterator token = tokens.begin(); |
785 | | |
786 | | if (*token != "add_engine") |
787 | | return false; |
788 | | ++token; |
789 | | |
790 | | engine.name = *token; ++token; |
791 | | engine.desc = *token; ++token; |
792 | | engine.enable = (*token == "yes"); ++token; |
793 | | if (token != tokens.end()) |
794 | | engine.subEngines = tokenize(*token); |
795 | | |
796 | | return true; |
797 | | } |
798 | | |
799 | | TokenList tokenize(const std::string &input) { |
800 | | TokenList result; |
801 | | |
802 | | std::string::size_type sIdx = input.find_first_not_of(" \t"); |
803 | | std::string::size_type nIdx = std::string::npos; |
804 | | |
805 | | if (sIdx == std::string::npos) |
806 | | return result; |
807 | | |
808 | | do { |
809 | | if (input.at(sIdx) == '\"') { |
810 | | ++sIdx; |
811 | | nIdx = input.find_first_of('\"', sIdx); |
812 | | } else { |
813 | | nIdx = input.find_first_of(' ', sIdx); |
814 | | } |
815 | | |
816 | | if (nIdx != std::string::npos) { |
817 | | result.push_back(input.substr(sIdx, nIdx - sIdx)); |
818 | | sIdx = input.find_first_not_of(" \t", nIdx + 1); |
819 | | } else { |
820 | | result.push_back(input.substr(sIdx)); |
821 | | break; |
822 | | } |
823 | | } while (sIdx != std::string::npos); |
824 | | |
825 | | return result; |
826 | | } |
827 | | } // End of anonymous namespace |
828 | | |
829 | | namespace { |
830 | | const Feature s_features[] = { |
831 | | // Libraries |
832 | | { "libz", "USE_ZLIB", "zlib.lib", true, "zlib (compression) support" }, |
833 | | { "mad", "USE_MAD", "libmad.lib", true, "libmad (MP3) support" }, |
834 | | { "vorbis", "USE_VORBIS", "libvorbisfile_static.lib libvorbis_static.lib libogg_static.lib", true, "Ogg Vorbis support" }, |
835 | | { "flac", "USE_FLAC", "libFLAC_static.lib", true, "FLAC support" }, |
836 | | { "theoradec", "USE_THEORADEC", "libtheora_static.lib", true, "Theora decoder support" }, |
837 | | { "mpeg2", "USE_MPEG2", "libmpeg2.lib", false, "mpeg2 codec for cutscenes" }, |
838 | | |
839 | | // ScummVM feature flags |
840 | | { "scalers", "USE_SCALERS", "", true, "Scalers" }, |
841 | | { "hqscalers", "USE_HQ_SCALERS", "", true, "HQ scalers" }, |
842 | | { "16bit", "USE_RGB_COLOR", "", true, "16bit color support" }, |
843 | | { "mt32emu", "USE_MT32EMU", "", true, "integrated MT-32 emulator" }, |
844 | | { "nasm", "USE_NASM", "", true, "IA-32 assembly support" }, // This feature is special in the regard, that it needs additional handling. |
845 | | { "translation", "USE_TRANSLATION", "", true, "Translation support" }, |
846 | | { "langdetect", "USE_DETECTLANG", "", true, "System language detection support" } // This feature actually depends on "translation", there |
847 | | // is just no current way of properly detecting this... |
848 | | }; |
849 | | } // End of anonymous namespace |
850 | | |
851 | | FeatureList getAllFeatures() { |
852 | | const size_t featureCount = sizeof(s_features) / sizeof(s_features[0]); |
853 | | |
854 | | FeatureList features; |
855 | | for (size_t i = 0; i < featureCount; ++i) |
856 | | features.push_back(s_features[i]); |
857 | | |
858 | | return features; |
859 | | } |
860 | | |
861 | | StringList getFeatureDefines(const FeatureList &features) { |
862 | | StringList defines; |
863 | | |
864 | | for (FeatureList::const_iterator i = features.begin(); i != features.end(); ++i) { |
865 | | if (i->enable && i->define && i->define[0]) |
866 | | defines.push_back(i->define); |
867 | | } |
868 | | |
869 | | return defines; |
870 | | } |
871 | | |
872 | | StringList getFeatureLibraries(const FeatureList &features) { |
873 | | StringList libraries; |
874 | | |
875 | | for (FeatureList::const_iterator i = features.begin(); i != features.end(); ++i) { |
876 | | if (i->enable && i->libraries && i->libraries[0]) { |
877 | | StringList fLibraries = tokenize(i->libraries); |
878 | | libraries.splice(libraries.end(), fLibraries); |
879 | | } |
880 | | } |
881 | | |
882 | | return libraries; |
883 | | } |
884 | | |
885 | | bool setFeatureBuildState(const std::string &name, FeatureList &features, bool enable) { |
886 | | FeatureList::iterator i = std::find(features.begin(), features.end(), name); |
887 | | if (i != features.end()) { |
888 | | i->enable = enable; |
889 | | return true; |
890 | | } else { |
891 | | return false; |
892 | | } |
893 | | } |
894 | | |
895 | | namespace { |
896 | | typedef std::map<std::string, std::string> UUIDMap; |
897 | | |
898 | | /** |
899 | | * Creates an UUID for every enabled engine of the |
900 | | * passed build description. |
901 | | * |
902 | | * @param setup Description of the desired build. |
903 | | * @return A map, which includes UUIDs for all enabled engines. |
904 | | */ |
905 | | UUIDMap createUUIDMap(const BuildSetup &setup); |
906 | | |
907 | | /** |
908 | | * Creates an UUID and returns it in string representation. |
909 | | * |
910 | | * @return A new UUID as string. |
911 | | */ |
912 | | std::string createUUID(); |
913 | | |
914 | | UUIDMap createUUIDMap(const BuildSetup &setup) { |
915 | | UUIDMap result; |
916 | | |
917 | | for (EngineDescList::const_iterator i = setup.engines.begin(); i != setup.engines.end(); ++i) { |
918 | | if (!i->enable || isSubEngine(i->name, setup.engines)) |
919 | | continue; |
920 | | |
921 | | result[i->name] = createUUID(); |
922 | | } |
923 | | |
924 | | return result; |
925 | | } |
926 | | |
927 | | std::string createUUID() { |
928 | | #if defined(_WIN32) || defined(WIN32) |
929 | | UUID uuid; |
930 | | if (UuidCreate(&uuid) != RPC_S_OK) |
931 | | error("UuidCreate failed"); |
932 | | |
933 | | unsigned char *string = 0; |
934 | | if (UuidToStringA(&uuid, &string) != RPC_S_OK) |
935 | | error("UuidToStringA failed"); |
936 | | |
937 | | std::string result = std::string((char *)string); |
938 | | std::transform(result.begin(), result.end(), result.begin(), toupper); |
939 | | RpcStringFreeA(&string); |
940 | | return result; |
941 | | #else |
942 | | unsigned char uuid[16]; |
943 | | |
944 | | for (int i = 0; i < 16; ++i) |
945 | | uuid[i] = (unsigned char)((std::rand() / (double)(RAND_MAX)) * 0xFF); |
946 | | |
947 | | uuid[8] &= 0xBF; uuid[8] |= 0x80; |
948 | | uuid[6] &= 0x4F; uuid[6] |= 0x40; |
949 | | |
950 | | std::stringstream uuidString; |
951 | | uuidString << std::hex << std::uppercase << std::setfill('0'); |
952 | | for (int i = 0; i < 16; ++i) { |
953 | | uuidString << std::setw(2) << (int)uuid[i]; |
954 | | if (i == 3 || i == 5 || i == 7 || i == 9) { |
955 | | uuidString << std::setw(0) << '-'; |
956 | | } |
957 | | } |
958 | | |
959 | | return uuidString.str(); |
960 | | #endif |
961 | | } |
962 | | |
963 | | ////////////////////////////////////////////////////////////////////////// |
964 | | // Project Provider methods |
965 | | ////////////////////////////////////////////////////////////////////////// |
966 | | ProjectProvider::ProjectProvider(const int version, std::string global_warnings, std::map<std::string, std::string> project_warnings) |
967 | | : _version(version), _globalWarnings(global_warnings), _projectWarnings(project_warnings) { |
968 | | } |
969 | | |
970 | | void ProjectProvider::createMSVCProject(const BuildSetup &setup) { |
971 | | _uuidMap = createUUIDMap(setup); |
972 | | |
973 | | // We also need to add the UUID of the main project file. |
974 | | const std::string svmUUID = _uuidMap["scummvm"] = createUUID(); |
975 | | |
976 | | createScummVMSolution(setup); |
977 | | |
978 | | StringList in, ex; |
979 | | |
980 | | // Create engine project files |
981 | | for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) { |
982 | | if (i->first == "scummvm") |
983 | | continue; |
984 | | |
985 | | in.clear(); ex.clear(); |
986 | | const std::string moduleDir = setup.srcDir + "/engines/" + i->first; |
987 | | |
988 | | createModuleList(moduleDir, setup.defines, in, ex); |
989 | | createProjectFile(i->first, i->second, setup, moduleDir, in, ex); |
990 | | } |
991 | | |
992 | | // Last but not least create the main ScummVM project file. |
993 | | in.clear(); ex.clear(); |
994 | | |
995 | | // File list for the ScummVM project file |
996 | | createModuleList(setup.srcDir + "/backends", setup.defines, in, ex); |
997 | | createModuleList(setup.srcDir + "/backends/platform/sdl", setup.defines, in, ex); |
998 | | createModuleList(setup.srcDir + "/base", setup.defines, in, ex); |
999 | | createModuleList(setup.srcDir + "/common", setup.defines, in, ex); |
1000 | | createModuleList(setup.srcDir + "/engines", setup.defines, in, ex); |
1001 | | createModuleList(setup.srcDir + "/graphics", setup.defines, in, ex); |
1002 | | createModuleList(setup.srcDir + "/gui", setup.defines, in, ex); |
1003 | | createModuleList(setup.srcDir + "/sound", setup.defines, in, ex); |
1004 | | createModuleList(setup.srcDir + "/sound/softsynth/mt32", setup.defines, in, ex); |
1005 | | |
1006 | | // Resource files |
1007 | | in.push_back(setup.srcDir + "/icons/scummvm.ico"); |
1008 | | in.push_back(setup.srcDir + "/dists/scummvm.rc"); |
1009 | | |
1010 | | // Various text files |
1011 | | in.push_back(setup.srcDir + "/AUTHORS"); |
1012 | | in.push_back(setup.srcDir + "/COPYING"); |
1013 | | in.push_back(setup.srcDir + "/COPYING.LGPL"); |
1014 | | in.push_back(setup.srcDir + "/COPYRIGHT"); |
1015 | | in.push_back(setup.srcDir + "/NEWS"); |
1016 | | in.push_back(setup.srcDir + "/README"); |
1017 | | in.push_back(setup.srcDir + "/TODO"); |
1018 | | |
1019 | | // Create the scummvm project file. |
1020 | | createProjectFile("scummvm", svmUUID, setup, setup.srcDir, in, ex); |
1021 | | |
1022 | | // Create the global property file |
1023 | | createGlobalProp(setup); |
1024 | | |
1025 | | // Create the configuration property files (for Debug and Release with 32 and 64bits versions) |
1026 | | createBuildProp(setup, true, false); |
1027 | | createBuildProp(setup, true, true); |
1028 | | createBuildProp(setup, false, false); |
1029 | | createBuildProp(setup, false, true); |
1030 | | } |
1031 | | |
1032 | | void ProjectProvider::createScummVMSolution(const BuildSetup &setup) { |
1033 | | UUIDMap::const_iterator svmUUID = _uuidMap.find("scummvm"); |
1034 | | if (svmUUID == _uuidMap.end()) |
1035 | | error("No UUID for \"scummvm\" project created"); |
1036 | | |
1037 | | const std::string svmProjectUUID = svmUUID->second; |
1038 | | assert(!svmProjectUUID.empty()); |
1039 | | |
1040 | | std::string solutionUUID = createUUID(); |
1041 | | |
1042 | | std::ofstream solution((setup.outputDir + '/' + "scummvm.sln").c_str()); |
1043 | | if (!solution) |
1044 | | error("Could not open \"" + setup.outputDir + '/' + "scummvm.sln\" for writing"); |
1045 | | |
1046 | | solution << "Microsoft Visual Studio Solution File, Format Version " << _version + 1 << ".00\n"; |
1047 | | solution << "# Visual Studio " << getVisualStudioVersion() << "\n"; |
1048 | | |
1049 | | solution << "Project(\"{" << solutionUUID << "}\") = \"scummvm\", \"scummvm" << getProjectExtension() << "\", \"{" << svmProjectUUID << "}\"\n"; |
1050 | | |
1051 | | // Project dependencies are moved to vcxproj files in Visual Studio 2010 |
1052 | | if (_version < 10) |
1053 | | writeReferences(solution); |
1054 | | |
1055 | | solution << "EndProject\n"; |
1056 | | |
1057 | | // Note we assume that the UUID map only includes UUIDs for enabled engines! |
1058 | | for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) { |
1059 | | if (i->first == "scummvm") |
1060 | | continue; |
1061 | | |
1062 | | solution << "Project(\"{" << solutionUUID << "}\") = \"" << i->first << "\", \"" << i->first << getProjectExtension() << "\", \"{" << i->second << "}\"\n" |
1063 | | << "EndProject\n"; |
1064 | | } |
1065 | | |
1066 | | solution << "Global\n" |
1067 | | "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n" |
1068 | | "\t\tDebug|Win32 = Debug|Win32\n" |
1069 | | "\t\tRelease|Win32 = Release|Win32\n" |
1070 | | "\t\tDebug|x64 = Debug|x64\n" |
1071 | | "\t\tRelease|x64 = Release|x64\n" |
1072 | | "\tEndGlobalSection\n" |
1073 | | "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n"; |
1074 | | |
1075 | | for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) { |
1076 | | solution << "\t\t{" << i->second << "}.Debug|Win32.ActiveCfg = Debug|Win32\n" |
1077 | | << "\t\t{" << i->second << "}.Debug|Win32.Build.0 = Debug|Win32\n" |
1078 | | << "\t\t{" << i->second << "}.Release|Win32.ActiveCfg = Release|Win32\n" |
1079 | | << "\t\t{" << i->second << "}.Release|Win32.Build.0 = Release|Win32\n" |
1080 | | << "\t\t{" << i->second << "}.Debug|x64.ActiveCfg = Debug|x64\n" |
1081 | | << "\t\t{" << i->second << "}.Debug|x64.Build.0 = Debug|x64\n" |
1082 | | << "\t\t{" << i->second << "}.Release|x64.ActiveCfg = Release|x64\n" |
1083 | | << "\t\t{" << i->second << "}.Release|x64.Build.0 = Release|x64\n"; |
1084 | | } |
1085 | | |
1086 | | solution << "\tEndGlobalSection\n" |
1087 | | "\tGlobalSection(SolutionProperties) = preSolution\n" |
1088 | | "\t\tHideSolutionNode = FALSE\n" |
1089 | | "\tEndGlobalSection\n" |
1090 | | "EndGlobal\n"; |
1091 | | } |
1092 | | |
1093 | | /** |
1094 | | * Gets a proper sequence of \t characters for the given |
1095 | | * indentation level. |
1096 | | * |
1097 | | * For example with an indentation level of 2 this will |
1098 | | * produce: |
1099 | | * \t\t |
1100 | | * |
1101 | | * @param indentation The indentation level |
1102 | | * @return Sequence of \t characters. |
1103 | | */ |
1104 | | std::string getIndent(const int indentation) { |
1105 | | std::string result; |
1106 | | for (int i = 0; i < indentation; ++i) |
1107 | | result += '\t'; |
1108 | | return result; |
1109 | | } |
1110 | | |
1111 | | /** |
1112 | | * Splits a file name into name and extension. |
1113 | | * The file name must be only the filename, no |
1114 | | * additional path name. |
1115 | | * |
1116 | | * @param fileName Filename to split |
1117 | | * @param name Reference to a string, where to store the name. |
1118 | | * @param ext Reference to a string, where to store the extension. |
1119 | | */ |
1120 | | void splitFilename(const std::string &fileName, std::string &name, std::string &ext) { |
1121 | | const std::string::size_type dot = fileName.find_last_of('.'); |
1122 | | name = (dot == std::string::npos) ? fileName : fileName.substr(0, dot); |
1123 | | ext = (dot == std::string::npos) ? std::string() : fileName.substr(dot + 1); |
1124 | | } |
1125 | | |
1126 | | /** |
1127 | | * Checks whether the given file will produce an object file or not. |
1128 | | * |
1129 | | * @param fileName Name of the file. |
1130 | | * @return "true" when it will produce a file, "false" otherwise. |
1131 | | */ |
1132 | | bool producesObjectFile(const std::string &fileName) { |
1133 | | std::string n, ext; |
1134 | | splitFilename(fileName, n, ext); |
1135 | | |
1136 | | if (ext == "cpp" || ext == "c" || ext == "asm") |
1137 | | return true; |
1138 | | else |
1139 | | return false; |
1140 | | } |
1141 | | |
1142 | | /** |
1143 | | * Checks whether the give file in the specified directory is present in the given |
1144 | | * file list. |
1145 | | * |
1146 | | * This function does as special match against the file list. It will not take file |
1147 | | * extensions into consideration, when the extension of a file in the specified |
1148 | | * directory is one of "h", "cpp", "c" or "asm". |
1149 | | * |
1150 | | * @param dir Parent directory of the file. |
1151 | | * @param fileName File name to match. |
1152 | | * @param fileList List of files to match against. |
1153 | | * @return "true" when the file is in the list, "false" otherwise. |
1154 | | */ |
1155 | | bool isInList(const std::string &dir, const std::string &fileName, const StringList &fileList) { |
1156 | | std::string compareName, extensionName; |
1157 | | splitFilename(fileName, compareName, extensionName); |
1158 | | |
1159 | | if (!extensionName.empty()) |
1160 | | compareName += '.'; |
1161 | | |
1162 | | for (StringList::const_iterator i = fileList.begin(); i != fileList.end(); ++i) { |
1163 | | if (i->compare(0, dir.size(), dir)) |
1164 | | continue; |
1165 | | |
1166 | | // When no comparison name is given, we try to match whether a subset of |
1167 | | // the given directory should be included. To do that we must assure that |
1168 | | // the first character after the substring, having the same size as dir, must |
1169 | | // be a path delimiter. |
1170 | | if (compareName.empty()) { |
1171 | | if (i->size() >= dir.size() + 1 && i->at(dir.size()) == '/') |
1172 | | return true; |
1173 | | else |
1174 | | continue; |
1175 | | } |
1176 | | |
1177 | | const std::string lastPathComponent = getLastPathComponent(*i); |
1178 | | if (!producesObjectFile(fileName) && extensionName != "h") { |
1179 | | if (fileName == lastPathComponent) |
1180 | | return true; |
1181 | | } else { |
1182 | | if (!lastPathComponent.compare(0, compareName.size(), compareName)) |
1183 | | return true; |
1184 | | } |
1185 | | } |
1186 | | |
1187 | | return false; |
1188 | | } |
1189 | | |
1190 | | /** |
1191 | | * A strict weak compare predicate for sorting a list of |
1192 | | * "FileNode *" entries. |
1193 | | * |
1194 | | * It will sort directory nodes before file nodes. |
1195 | | * |
1196 | | * @param l Left-hand operand. |
1197 | | * @param r Right-hand operand. |
1198 | | * @return "true" if and only if l should be sorted before r. |
1199 | | */ |
1200 | | bool compareNodes(const FileNode *l, const FileNode *r) { |
1201 | | if (!l) { |
1202 | | return false; |
1203 | | } else if (!r) { |
1204 | | return true; |
1205 | | } else { |
1206 | | if (l->children.empty() && !r->children.empty()) { |
1207 | | return false; |
1208 | | } else if (!l->children.empty() && r->children.empty()) { |
1209 | | return true; |
1210 | | } else { |
1211 | | return l->name < r->name; |
1212 | | } |
1213 | | } |
1214 | | } |
1215 | | |
1216 | | /** |
1217 | | * Returns a list of all files and directories in the specified |
1218 | | * path. |
1219 | | * |
1220 | | * @param dir Directory which should be listed. |
1221 | | * @return List of all children. |
1222 | | */ |
1223 | | FileList listDirectory(const std::string &dir) { |
1224 | | FileList result; |
1225 | | #if defined(_WIN32) || defined(WIN32) |
1226 | | WIN32_FIND_DATA fileInformation; |
1227 | | HANDLE fileHandle = FindFirstFile((dir + "/*").c_str(), &fileInformation); |
1228 | | |
1229 | | if (fileHandle == INVALID_HANDLE_VALUE) |
1230 | | return result; |
1231 | | |
1232 | | do { |
1233 | | if (fileInformation.cFileName[0] == '.') |
1234 | | continue; |
1235 | | |
1236 | | result.push_back(FSNode(fileInformation.cFileName, (fileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)); |
1237 | | } while (FindNextFile(fileHandle, &fileInformation) == TRUE); |
1238 | | |
1239 | | FindClose(fileHandle); |
1240 | | #else |
1241 | | DIR *dirp = opendir(dir.c_str()); |
1242 | | struct dirent *dp = NULL; |
1243 | | |
1244 | | if (dirp == NULL) |
1245 | | return result; |
1246 | | |
1247 | | while ((dp = readdir(dirp)) != NULL) { |
1248 | | if (dp->d_name[0] == '.') |
1249 | | continue; |
1250 | | |
1251 | | struct stat st; |
1252 | | if (stat((dir + '/' + dp->d_name).c_str(), &st)) |
1253 | | continue; |
1254 | | |
1255 | | result.push_back(FSNode(dp->d_name, S_ISDIR(st.st_mode))); |
1256 | | } |
1257 | | #endif |
1258 | | return result; |
1259 | | } |
1260 | | |
1261 | | /** |
1262 | | * Scans the specified directory against files, which should be included |
1263 | | * in the project files. It will not include files present in the exclude list. |
1264 | | * |
1265 | | * @param dir Directory in which to search for files. |
1266 | | * @param includeList Files to include in the project. |
1267 | | * @param excludeList Files to exclude from the project. |
1268 | | * @return Returns a file node for the specific directory. |
1269 | | */ |
1270 | | FileNode *scanFiles(const std::string &dir, const StringList &includeList, const StringList &excludeList) { |
1271 | | FileList files = listDirectory(dir); |
1272 | | |
1273 | | if (files.empty()) |
1274 | | return 0; |
1275 | | |
1276 | | FileNode *result = new FileNode(dir); |
1277 | | assert(result); |
1278 | | |
1279 | | for (FileList::const_iterator i = files.begin(); i != files.end(); ++i) { |
1280 | | if (i->isDirectory) { |
1281 | | const std::string subDirName = dir + '/' + i->name; |
1282 | | if (!isInList(subDirName, std::string(), includeList)) |
1283 | | continue; |
1284 | | |
1285 | | FileNode *subDir = scanFiles(subDirName, includeList, excludeList); |
1286 | | |
1287 | | if (subDir) { |
1288 | | subDir->name = i->name; |
1289 | | result->children.push_back(subDir); |
1290 | | } |
1291 | | continue; |
1292 | | } |
1293 | | |
1294 | | if (isInList(dir, i->name, excludeList)) |
1295 | | continue; |
1296 | | |
1297 | | std::string name, ext; |
1298 | | splitFilename(i->name, name, ext); |
1299 | | |
1300 | | if (ext != "h") { |
1301 | | if (!isInList(dir, i->name, includeList)) |
1302 | | continue; |
1303 | | } |
1304 | | |
1305 | | FileNode *child = new FileNode(i->name); |
1306 | | assert(child); |
1307 | | result->children.push_back(child); |
1308 | | } |
1309 | | |
1310 | | if (result->children.empty()) { |
1311 | | delete result; |
1312 | | return 0; |
1313 | | } else { |
1314 | | result->children.sort(compareNodes); |
1315 | | return result; |
1316 | | } |
1317 | | } |
1318 | | |
1319 | | void ProjectProvider::createGlobalProp(const BuildSetup &setup) { |
1320 | | std::ofstream properties((setup.outputDir + '/' + "ScummVM_Global" + getPropertiesExtension()).c_str()); |
1321 | | if (!properties) |
1322 | | error("Could not open \"" + setup.outputDir + '/' + "ScummVM_Global" + getPropertiesExtension() + "\" for writing"); |
1323 | | |
1324 | | std::string defines; |
1325 | | for (StringList::const_iterator i = setup.defines.begin(); i != setup.defines.end(); ++i) { |
1326 | | if (i != setup.defines.begin()) |
1327 | | defines += ';'; |
1328 | | defines += *i; |
1329 | | } |
1330 | | |
1331 | | outputGlobalPropFile(properties, 32, defines, convertPathToWin(setup.filePrefix), true); |
1332 | | properties.close(); |
1333 | | |
1334 | | properties.open((setup.outputDir + '/' + "ScummVM_Global64" + getPropertiesExtension()).c_str()); |
1335 | | if (!properties) |
1336 | | error("Could not open \"" + setup.outputDir + '/' + "ScummVM_Global64" + getPropertiesExtension() + "\" for writing"); |
1337 | | |
1338 | | // HACK: We must disable the "nasm" feature for x64. To achieve that we must duplicate the feature list and |
1339 | | // recreate a define list. |
1340 | | FeatureList x64Features = setup.features; |
1341 | | setFeatureBuildState("nasm", x64Features, false); |
1342 | | StringList x64Defines = getFeatureDefines(x64Features); |
1343 | | StringList x64EngineDefines = getEngineDefines(setup.engines); |
1344 | | x64Defines.splice(x64Defines.end(), x64EngineDefines); |
1345 | | |
1346 | | defines.clear(); |
1347 | | for (StringList::const_iterator i = x64Defines.begin(); i != x64Defines.end(); ++i) { |
1348 | | if (i != x64Defines.begin()) |
1349 | | defines += ';'; |
1350 | | defines += *i; |
1351 | | } |
1352 | | |
1353 | | outputGlobalPropFile(properties, 64, defines, convertPathToWin(setup.filePrefix), false); |
1354 | | } |
1355 | | |
1356 | | void ProjectProvider::addFilesToProject(const std::string &dir, std::ofstream &projectFile, |
1357 | | const StringList &includeList, const StringList &excludeList, |
1358 | | const std::string &filePrefix) { |
1359 | | // Check for duplicate object file names |
1360 | | StringList duplicate; |
1361 | | |
1362 | | for (StringList::const_iterator i = includeList.begin(); i != includeList.end(); ++i) { |
1363 | | const std::string fileName = getLastPathComponent(*i); |
1364 | | |
1365 | | // Leave out non object file names. |
1366 | | if (fileName.size() < 2 || fileName.compare(fileName.size() - 2, 2, ".o")) |
1367 | | continue; |
1368 | | |
1369 | | // Check whether an duplicate has been found yet |
1370 | | if (std::find(duplicate.begin(), duplicate.end(), fileName) != duplicate.end()) |
1371 | | continue; |
1372 | | |
1373 | | // Search for duplicates |
1374 | | StringList::const_iterator j = i; ++j; |
1375 | | for (; j != includeList.end(); ++j) { |
1376 | | if (fileName == getLastPathComponent(*j)) { |
1377 | | duplicate.push_back(fileName); |
1378 | | break; |
1379 | | } |
1380 | | } |
1381 | | } |
1382 | | |
1383 | | FileNode *files = scanFiles(dir, includeList, excludeList); |
1384 | | |
1385 | | writeFileListToProject(*files, projectFile, 0, duplicate, std::string(), filePrefix + '/'); |
1386 | | |
1387 | | delete files; |
1388 | | } |
1389 | | |
1390 | | void ProjectProvider::createModuleList(const std::string &moduleDir, const StringList &defines, StringList &includeList, StringList &excludeList) { |
1391 | | const std::string moduleMkFile = moduleDir + "/module.mk"; |
1392 | | std::ifstream moduleMk(moduleMkFile.c_str()); |
1393 | | if (!moduleMk) |
1394 | | error(moduleMkFile + " is not present"); |
1395 | | |
1396 | | includeList.push_back(moduleMkFile); |
1397 | | |
1398 | | std::stack<bool> shouldInclude; |
1399 | | shouldInclude.push(true); |
1400 | | |
1401 | | bool hadModule = false; |
1402 | | std::string line; |
1403 | | while (true) { |
1404 | | std::getline(moduleMk, line); |
1405 | | |
1406 | | if (moduleMk.eof()) |
1407 | | break; |
1408 | | |
1409 | | if (moduleMk.fail()) |
1410 | | error("Failed while reading from " + moduleMkFile); |
1411 | | |
1412 | | TokenList tokens = tokenize(line); |
1413 | | if (tokens.empty()) |
1414 | | continue; |
1415 | | |
1416 | | TokenList::const_iterator i = tokens.begin(); |
1417 | | if (*i == "MODULE") { |
1418 | | if (hadModule) |
1419 | | error("More than one MODULE definition in " + moduleMkFile); |
1420 | | // Format: "MODULE := path/to/module" |
1421 | | if (tokens.size() < 3) |
1422 | | error("Malformed MODULE definition in " + moduleMkFile); |
1423 | | ++i; |
1424 | | if (*i != ":=") |
1425 | | error("Malformed MODULE definition in " + moduleMkFile); |
1426 | | ++i; |
1427 | | |
1428 | | std::string moduleRoot = unifyPath(*i); |
1429 | | if (moduleDir.compare(moduleDir.size() - moduleRoot.size(), moduleRoot.size(), moduleRoot)) |
1430 | | error("MODULE root " + moduleRoot + " does not match base dir " + moduleDir); |
1431 | | |
1432 | | hadModule = true; |
1433 | | } else if (*i == "MODULE_OBJS") { |
1434 | | if (tokens.size() < 3) |
1435 | | error("Malformed MODULE_OBJS definition in " + moduleMkFile); |
1436 | | ++i; |
1437 | | |
1438 | | // This is not exactly correct, for example an ":=" would usually overwrite |
1439 | | // all already added files, but since we do only save the files inside |
1440 | | // includeList or excludeList currently, we couldn't handle such a case easily. |
1441 | | // (includeList and excludeList should always preserve their entries, not added |
1442 | | // by this function, thus we can't just clear them on ":=" or "="). |
1443 | | // But hopefully our module.mk files will never do such things anyway. |
1444 | | if (*i != ":=" && *i != "+=" && *i != "=") |
1445 | | error("Malformed MODULE_OBJS definition in " + moduleMkFile); |
1446 | | |
1447 | | ++i; |
1448 | | |
1449 | | while (i != tokens.end()) { |
1450 | | if (*i == "\\") { |
1451 | | std::getline(moduleMk, line); |
1452 | | tokens = tokenize(line); |
1453 | | i = tokens.begin(); |
1454 | | } else { |
1455 | | if (shouldInclude.top()) |
1456 | | includeList.push_back(moduleDir + "/" + unifyPath(*i)); |
1457 | | else |
1458 | | excludeList.push_back(moduleDir + "/" + unifyPath(*i)); |
1459 | | ++i; |
1460 | | } |
1461 | | } |
1462 | | } else if (*i == "ifdef") { |
1463 | | if (tokens.size() < 2) |
1464 | | error("Malformed ifdef in " + moduleMkFile); |
1465 | | ++i; |
1466 | | |
1467 | | if (std::find(defines.begin(), defines.end(), *i) == defines.end()) |
1468 | | shouldInclude.push(false); |
1469 | | else |
1470 | | shouldInclude.push(true); |
1471 | | } else if (*i == "ifndef") { |
1472 | | if (tokens.size() < 2) |
1473 | | error("Malformed ifndef in " + moduleMkFile); |
1474 | | ++i; |
1475 | | |
1476 | | if (std::find(defines.begin(), defines.end(), *i) == defines.end()) |
1477 | | shouldInclude.push(true); |
1478 | | else |
1479 | | shouldInclude.push(false); |
1480 | | } else if (*i == "else") { |
1481 | | shouldInclude.top() = !shouldInclude.top(); |
1482 | | } else if (*i == "endif") { |
1483 | | if (shouldInclude.size() <= 1) |
1484 | | error("endif without ifdef found in " + moduleMkFile); |
1485 | | shouldInclude.pop(); |
1486 | | } else if (*i == "elif") { |
1487 | | error("Unsupported operation 'elif' in " + moduleMkFile); |
1488 | | } else if (*i == "ifeq") { |
1489 | | //XXX |
1490 | | shouldInclude.push(false); |
1491 | | } |
1492 | | } |
1493 | | |
1494 | | if (shouldInclude.size() != 1) |
1495 | | error("Malformed file " + moduleMkFile); |
1496 | | } |
1497 | | |
1498 | | ////////////////////////////////////////////////////////////////////////// |
1499 | | // Visual Studio Provider |
1500 | | ////////////////////////////////////////////////////////////////////////// |
1501 | | |
1502 | | VisualStudioProvider::VisualStudioProvider(const int version, std::string global_warnings, std::map<std::string, std::string> project_warnings) |
1503 | | : ProjectProvider(version, global_warnings, project_warnings) { |
1504 | | } |
1505 | | |
1506 | | const char *VisualStudioProvider::getProjectExtension() { |
1507 | | return ".vcproj"; |
1508 | | } |
1509 | | |
1510 | | const char *VisualStudioProvider::getPropertiesExtension() { |
1511 | | return ".vsprops"; |
1512 | | } |
1513 | | |
1514 | | int VisualStudioProvider::getVisualStudioVersion() { |
1515 | | if (_version == 9) |
1516 | | return 2008; |
1517 | | |
1518 | | if (_version == 8) |
1519 | | return 2005; |
1520 | | |
1521 | | error("Unsupported version passed to createScummVMSolution"); |
1522 | | return 0; |
1523 | | } |
1524 | | |
1525 | | void VisualStudioProvider::createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir, |
1526 | | const StringList &includeList, const StringList &excludeList) { |
1527 | | const std::string projectFile = setup.outputDir + '/' + name + getProjectExtension(); |
1528 | | std::ofstream project(projectFile.c_str()); |
1529 | | if (!project) |
1530 | | error("Could not open \"" + projectFile + "\" for writing"); |
1531 | | |
1532 | | project << "<?xml version=\"1.0\" encoding=\"windows-1252\"?>\n" |
1533 | | "<VisualStudioProject\n" |
1534 | | "\tProjectType=\"Visual C++\"\n" |
1535 | | "\tVersion=\"" << _version << ".00\"\n" |
1536 | | "\tName=\"" << name << "\"\n" |
1537 | | "\tProjectGUID=\"{" << uuid << "}\"\n" |
1538 | | "\tRootNamespace=\"" << name << "\"\n" |
1539 | | "\tKeyword=\"Win32Proj\"\n"; |
1540 | | |
1541 | | if (_version >= 9) |
1542 | | project << "\tTargetFrameworkVersion=\"131072\"\n"; |
1543 | | |
1544 | | project << "\t>\n" |
1545 | | "\t<Platforms>\n" |
1546 | | "\t\t<Platform Name=\"Win32\" />\n" |
1547 | | "\t\t<Platform Name=\"x64\" />\n" |
1548 | | "\t</Platforms>\n" |
1549 | | "\t<Configurations>\n"; |
1550 | | |
1551 | | // Check for project-specific warnings: |
1552 | | std::map<std::string, std::string>::iterator warnings = _projectWarnings.find(name); |
1553 | | |
1554 | | if (name == "scummvm") { |
1555 | | std::string libraries; |
1556 | | |
1557 | | for (StringList::const_iterator i = setup.libraries.begin(); i != setup.libraries.end(); ++i) |
1558 | | libraries += ' ' + *i; |
1559 | | |
1560 | | // Win32 |
1561 | | project << "\t\t<Configuration Name=\"Debug|Win32\" ConfigurationType=\"1\" InheritedPropertySheets=\".\\ScummVM_Debug.vsprops\">\n" |
1562 | | "\t\t\t<Tool\tName=\"VCCLCompilerTool\" DisableLanguageExtensions=\"false\" />\n" |
1563 | | "\t\t\t<Tool\tName=\"VCLinkerTool\" OutputFile=\"$(OutDir)/scummvm.exe\"\n" |
1564 | | "\t\t\t\tAdditionalDependencies=\"" << libraries << "\"\n" |
1565 | | "\t\t\t/>\n" |
1566 | | "\t\t</Configuration>\n" |
1567 | | "\t\t<Configuration Name=\"Release|Win32\" ConfigurationType=\"1\" InheritedPropertySheets=\".\\ScummVM_Release.vsprops\">\n" |
1568 | | "\t\t\t<Tool\tName=\"VCCLCompilerTool\" DisableLanguageExtensions=\"false\" />\n" |
1569 | | "\t\t\t<Tool\tName=\"VCLinkerTool\" OutputFile=\"$(OutDir)/scummvm.exe\"\n" |
1570 | | "\t\t\t\tAdditionalDependencies=\"" << libraries << "\"\n" |
1571 | | "\t\t\t/>\n" |
1572 | | "\t\t</Configuration>\n"; |
1573 | | |
1574 | | // x64 |
1575 | | // For 'x64' we must disable NASM support. Usually we would need to disable the "nasm" feature for that and |
1576 | | // re-create the library list, BUT since NASM doesn't link any additional libraries, we can just use the |
1577 | | // libraries list created for IA-32. If that changes in the future, we need to adjust this part! |
1578 | | project << "\t\t<Configuration Name=\"Debug|x64\" ConfigurationType=\"1\" InheritedPropertySheets=\".\\ScummVM_Debug64.vsprops\">\n" |
1579 | | "\t\t\t<Tool\tName=\"VCCLCompilerTool\" DisableLanguageExtensions=\"false\" />\n" |
1580 | | "\t\t\t<Tool\tName=\"VCLinkerTool\" OutputFile=\"$(OutDir)/scummvm.exe\"\n" |
1581 | | "\t\t\t\tAdditionalDependencies=\"" << libraries << "\"\n" |
1582 | | "\t\t\t/>\n" |
1583 | | "\t\t</Configuration>\n" |
1584 | | "\t\t<Configuration Name=\"Release|x64\" ConfigurationType=\"1\" InheritedPropertySheets=\".\\ScummVM_Release64.vsprops\">\n" |
1585 | | "\t\t\t<Tool\tName=\"VCCLCompilerTool\" DisableLanguageExtensions=\"false\" />\n" |
1586 | | "\t\t\t<Tool\tName=\"VCLinkerTool\" OutputFile=\"$(OutDir)/scummvm.exe\"\n" |
1587 | | "\t\t\t\tAdditionalDependencies=\"" << libraries << "\"\n" |
1588 | | "\t\t\t/>\n" |
1589 | | "\t\t</Configuration>\n"; |
1590 | | } else if (warnings != _projectWarnings.end()) { |
1591 | | // Win32 |
1592 | | project << "\t\t<Configuration Name=\"Debug|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug.vsprops\">\n" |
1593 | | "\t\t\t<Tool Name=\"VCCLCompilerTool\" DisableSpecificWarnings=\"" << warnings->second << "\" />\n" |
1594 | | "\t\t</Configuration>\n" |
1595 | | "\t\t<Configuration Name=\"Release|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release.vsprops\">\n" |
1596 | | "\t\t\t<Tool Name=\"VCCLCompilerTool\" DisableSpecificWarnings=\"" << warnings->second << "\" />\n" |
1597 | | "\t\t</Configuration>\n"; |
1598 | | // x64 |
1599 | | project << "\t\t<Configuration Name=\"Debug|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug64.vsprops\">\n" |
1600 | | "\t\t\t<Tool Name=\"VCCLCompilerTool\" DisableSpecificWarnings=\"" << warnings->second << "\" />\n" |
1601 | | "\t\t</Configuration>\n" |
1602 | | "\t\t<Configuration Name=\"Release|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release64.vsprops\">\n" |
1603 | | "\t\t\t<Tool Name=\"VCCLCompilerTool\" DisableSpecificWarnings=\"" << warnings->second << "\" />\n" |
1604 | | "\t\t</Configuration>\n"; |
1605 | | } else if (name == "sword25") { |
1606 | | // Win32 |
1607 | | project << "\t\t<Configuration Name=\"Debug|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug.vsprops\">\n" |
1608 | | "\t\t\t<Tool Name=\"VCCLCompilerTool\" DisableLanguageExtensions=\"false\" />\n" |
1609 | | "\t\t</Configuration>\n" |
1610 | | "\t\t<Configuration Name=\"Release|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release.vsprops\" />\n"; |
1611 | | // x64 |
1612 | | project << "\t\t<Configuration Name=\"Debug|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug64.vsprops\">\n" |
1613 | | "\t\t\t<Tool Name=\"VCCLCompilerTool\" DisableLanguageExtensions=\"false\" />\n" |
1614 | | "\t\t</Configuration>\n" |
1615 | | "\t\t<Configuration Name=\"Release|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release64.vsprops\" />\n"; |
1616 | | } else if (name == "tinsel") { |
1617 | | // Win32 |
1618 | | project << "\t\t<Configuration Name=\"Debug|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug.vsprops\">\n" |
1619 | | "\t\t\t<Tool Name=\"VCCLCompilerTool\" DebugInformationFormat=\"3\" />\n" |
1620 | | "\t\t</Configuration>\n" |
1621 | | "\t\t<Configuration Name=\"Release|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release.vsprops\" />\n"; |
1622 | | // x64 |
1623 | | project << "\t\t<Configuration Name=\"Debug|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug64.vsprops\">\n" |
1624 | | "\t\t\t<Tool Name=\"VCCLCompilerTool\" DebugInformationFormat=\"3\" />\n" |
1625 | | "\t\t</Configuration>\n" |
1626 | | "\t\t<Configuration Name=\"Release|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release64.vsprops\" />\n"; |
1627 | | } else { |
1628 | | // Win32 |
1629 | | project << "\t\t<Configuration Name=\"Debug|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug.vsprops\" />\n" |
1630 | | "\t\t<Configuration Name=\"Release|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release.vsprops\" />\n"; |
1631 | | // x64 |
1632 | | project << "\t\t<Configuration Name=\"Debug|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug64.vsprops\" />\n" |
1633 | | "\t\t<Configuration Name=\"Release|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release64.vsprops\" />\n"; |
1634 | | } |
1635 | | project << "\t</Configurations>\n" |
1636 | | "\t<Files>\n"; |
1637 | | |
1638 | | std::string modulePath; |
1639 | | if (!moduleDir.compare(0, setup.srcDir.size(), setup.srcDir)) { |
1640 | | modulePath = moduleDir.substr(setup.srcDir.size()); |
1641 | | if (!modulePath.empty() && modulePath.at(0) == '/') |
1642 | | modulePath.erase(0, 1); |
1643 | | } |
1644 | | |
1645 | | if (modulePath.size()) |
1646 | | addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix + '/' + modulePath); |
1647 | | else |
1648 | | addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix); |
1649 | | |
1650 | | project << "\t</Files>\n" |
1651 | | "</VisualStudioProject>\n"; |
1652 | | } |
1653 | | |
1654 | | void VisualStudioProvider::writeReferences(std::ofstream &output) { |
1655 | | output << "\tProjectSection(ProjectDependencies) = postProject\n"; |
1656 | | |
1657 | | for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) { |
1658 | | if (i->first == "scummvm") |
1659 | | continue; |
1660 | | |
1661 | | output << "\t\t{" << i->second << "} = {" << i->second << "}\n"; |
1662 | | } |
1663 | | |
1664 | | output << "\tEndProjectSection\n"; |
1665 | | } |
1666 | | |
1667 | | void VisualStudioProvider::outputGlobalPropFile(std::ofstream &properties, int bits, const std::string &defines, const std::string &prefix, bool isWin32) { |
1668 | | properties << "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\n" |
1669 | | "<VisualStudioPropertySheet\n" |
1670 | | "\tProjectType=\"Visual C++\"\n" |
1671 | | "\tVersion=\"8.00\"\n" |
1672 | | "\tName=\"ScummVM_Global\"\n" |
1673 | | "\tOutputDirectory=\"$(ConfigurationName)" << bits << "\"\n" |
1674 | | "\tIntermediateDirectory=\"$(ConfigurationName)" << bits << "/$(ProjectName)\"\n" |
1675 | | "\t>\n" |
1676 | | "\t<Tool\n" |
1677 | | "\t\tName=\"VCCLCompilerTool\"\n" |
1678 | | "\t\tDisableLanguageExtensions=\"true\"\n" |
1679 | | "\t\tDisableSpecificWarnings=\"" << _globalWarnings << "\"\n" |
1680 | | "\t\tAdditionalIncludeDirectories=\"" << prefix << ";" << prefix << "\\engines;$(SCUMMVM_LIBS)\\include\"\n" |
1681 | | "\t\tPreprocessorDefinitions=\"" << defines << "\"\n" |
1682 | | "\t\tExceptionHandling=\"0\"\n" |
1683 | | "\t\tRuntimeTypeInfo=\"false\"\n" |
1684 | | "\t\tWarningLevel=\"4\"\n" |
1685 | | "\t\tWarnAsError=\"false\"\n" |
1686 | | "\t\tCompileAs=\"0\"\n" |
1687 | | "\t\t/>\n" |
1688 | | "\t<Tool\n" |
1689 | | "\t\tName=\"VCLibrarianTool\"\n" |
1690 | | "\t\tIgnoreDefaultLibraryNames=\"\"\n" |
1691 | | "\t/>\n" |
1692 | | "\t<Tool\n" |
1693 | | "\t\tName=\"VCLinkerTool\"\n" |
1694 | | "\t\tIgnoreDefaultLibraryNames=\"\"\n" |
1695 | | "\t\tSubSystem=\"1\"\n" |
1696 | | "\t\tEntryPointSymbol=\"WinMainCRTStartup\"\n" |
1697 | | "\t\tAdditionalLibraryDirectories=\"$(SCUMMVM_LIBS)\\libs\\" << (isWin32 ? "x86" : "x64") << "\"\n" |
1698 | | "\t/>\n" |
1699 | | "\t<Tool\n" |
1700 | | "\t\tName=\"VCResourceCompilerTool\"\n" |
1701 | | "\t\tPreprocessorDefinitions=\"HAS_INCLUDE_SET\"\n" |
1702 | | "\t\tAdditionalIncludeDirectories=\"" << prefix << "\"\n" |
1703 | | "\t/>\n" |
1704 | | "</VisualStudioPropertySheet>\n"; |
1705 | | |
1706 | | properties.flush(); |
1707 | | } |
1708 | | |
1709 | | void VisualStudioProvider::createBuildProp(const BuildSetup &setup, bool isRelease, bool isWin32) { |
1710 | | const std::string outputType = (isRelease ? "Release" : "Debug"); |
1711 | | const std::string outputBitness = (isWin32 ? "32" : "64"); |
1712 | | |
1713 | | std::ofstream properties((setup.outputDir + '/' + "ScummVM_" + outputType + (isWin32 ? "" : "64") + getPropertiesExtension()).c_str()); |
1714 | | if (!properties) |
1715 | | error("Could not open \"" + setup.outputDir + '/' + "ScummVM_" + outputType + (isWin32 ? "" : "64") + getPropertiesExtension() + "\" for writing"); |
1716 | | |
1717 | | properties << "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\n" |
1718 | | "<VisualStudioPropertySheet\n" |
1719 | | "\tProjectType=\"Visual C++\"\n" |
1720 | | "\tVersion=\"8.00\"\n" |
1721 | | "\tName=\"ScummVM_" << outputType << outputBitness << "\"\n" |
1722 | | "\tInheritedPropertySheets=\".\\ScummVM_Global" << (isWin32 ? "" : "64") << ".vsprops\"\n" |
1723 | | "\t>\n" |
1724 | | "\t<Tool\n" |
1725 | | "\t\tName=\"VCCLCompilerTool\"\n"; |
1726 | | |
1727 | | if (isRelease) { |
1728 | | properties << "\t\tEnableIntrinsicFunctions=\"true\"\n" |
1729 | | "\t\tWholeProgramOptimization=\"true\"\n" |
1730 | | "\t\tPreprocessorDefinitions=\"WIN32;RELEASE_BUILD\"\n" |
1731 | | "\t\tStringPooling=\"true\"\n" |
1732 | | "\t\tBufferSecurityCheck=\"false\"\n" |
1733 | | "\t\tDebugInformationFormat=\"0\"\n" |
1734 | | "\t/>\n" |
1735 | | "\t<Tool\n" |
1736 | | "\t\tName=\"VCLinkerTool\"\n" |
1737 | | "\t\tLinkIncremental=\"1\"\n" |
1738 | | "\t\tIgnoreDefaultLibraryNames=\"\"\n" |
1739 | | "\t\tSetChecksum=\"true\"\n"; |
1740 | | } else { |
1741 | | properties << "\t\tOptimization=\"0\"\n" |
1742 | | "\t\tPreprocessorDefinitions=\"WIN32\"\n" |
1743 | | "\t\tMinimalRebuild=\"true\"\n" |
1744 | | "\t\tBasicRuntimeChecks=\"3\"\n" |
1745 | | "\t\tRuntimeLibrary=\"1\"\n" |
1746 | | "\t\tEnableFunctionLevelLinking=\"true\"\n" |
1747 | | "\t\tWarnAsError=\"false\"\n" |
1748 | | "\t\tDebugInformationFormat=\"" << (isWin32 ? "4" : "3") << "\"\n" // For x64 format "4" (Edit and continue) is not supported, thus we default to "3" |
1749 | | "\t/>\n" |
1750 | | "\t<Tool\n" |
1751 | | "\t\tName=\"VCLinkerTool\"\n" |
1752 | | "\t\tLinkIncremental=\"2\"\n" |
1753 | | "\t\tGenerateDebugInformation=\"true\"\n" |
1754 | | "\t\tIgnoreDefaultLibraryNames=\"libcmt.lib\"\n"; |
1755 | | } |
1756 | | |
1757 | | properties << "\t/>\n" |
1758 | | "</VisualStudioPropertySheet>\n"; |
1759 | | |
1760 | | properties.flush(); |
1761 | | properties.close(); |
1762 | | } |
1763 | | |
1764 | | void VisualStudioProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, |
1765 | | const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix) { |
1766 | | const std::string indentString = getIndent(indentation + 2); |
1767 | | |
1768 | | if (indentation) |
1769 | | projectFile << getIndent(indentation + 1) << "<Filter\tName=\"" << dir.name << "\">\n"; |
1770 | | |
1771 | | for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) { |
1772 | | const FileNode *node = *i; |
1773 | | |
1774 | | if (!node->children.empty()) { |
1775 | | writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/'); |
1776 | | } else { |
1777 | | if (producesObjectFile(node->name)) { |
1778 | | std::string name, ext; |
1779 | | splitFilename(node->name, name, ext); |
1780 | | const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), name + ".o") != duplicate.end()); |
1781 | | |
1782 | | if (ext == "asm") { |
1783 | | std::string objFileName = "$(IntDir)\\"; |
1784 | | if (isDuplicate) |
1785 | | objFileName += objPrefix; |
1786 | | objFileName += "$(InputName).obj"; |
1787 | | |
1788 | | const std::string toolLine = indentString + "\t\t<Tool Name=\"VCCustomBuildTool\" CommandLine=\"nasm.exe -f win32 -g -o "" + objFileName + "" "$(InputPath)"
\" Outputs=\"" + objFileName + "\" />\n"; |
1789 | | |
1790 | | // NASM is not supported for x64, thus we do not need to add additional entries here :-). |
1791 | | projectFile << indentString << "<File RelativePath=\"" << convertPathToWin(filePrefix + node->name) << "\">\n" |
1792 | | << indentString << "\t<FileConfiguration Name=\"Debug|Win32\">\n" |
1793 | | << toolLine |
1794 | | << indentString << "\t</FileConfiguration>\n" |
1795 | | << indentString << "\t<FileConfiguration Name=\"Release|Win32\">\n" |
1796 | | << toolLine |
1797 | | << indentString << "\t</FileConfiguration>\n" |
1798 | | << indentString << "</File>\n"; |
1799 | | } else { |
1800 | | if (isDuplicate) { |
1801 | | const std::string toolLine = indentString + "\t\t<Tool Name=\"VCCLCompilerTool\" ObjectFile=\"$(IntDir)\\" + objPrefix + "$(InputName).obj\" XMLDocumentationFileName=\"$(IntDir)\\" + objPrefix + "$(InputName).xdc\" />\n"; |
1802 | | |
1803 | | projectFile << indentString << "<File RelativePath=\"" << convertPathToWin(filePrefix + node->name) << "\">\n" |
1804 | | << indentString << "\t<FileConfiguration Name=\"Debug|Win32\">\n" |
1805 | | << toolLine |
1806 | | << indentString << "\t</FileConfiguration>\n" |
1807 | | << indentString << "\t<FileConfiguration Name=\"Release|Win32\">\n" |
1808 | | << toolLine |
1809 | | << indentString << "\t</FileConfiguration>\n" |
1810 | | << indentString << "\t<FileConfiguration Name=\"Debug|x64\">\n" |
1811 | | << toolLine |
1812 | | << indentString << "\t</FileConfiguration>\n" |
1813 | | << indentString << "\t<FileConfiguration Name=\"Release|x64\">\n" |
1814 | | << toolLine |
1815 | | << indentString << "\t</FileConfiguration>\n" |
1816 | | << indentString << "</File>\n"; |
1817 | | } else { |
1818 | | projectFile << indentString << "<File RelativePath=\"" << convertPathToWin(filePrefix + node->name) << "\" />\n"; |
1819 | | } |
1820 | | } |
1821 | | } else { |
1822 | | projectFile << indentString << "<File RelativePath=\"" << convertPathToWin(filePrefix + node->name) << "\" />\n"; |
1823 | | } |
1824 | | } |
1825 | | } |
1826 | | |
1827 | | if (indentation) |
1828 | | projectFile << getIndent(indentation + 1) << "</Filter>\n"; |
1829 | | } |
1830 | | |
1831 | | ////////////////////////////////////////////////////////////////////////// |
1832 | | // MSBuild Provider (Visual Studio 2010) |
1833 | | ////////////////////////////////////////////////////////////////////////// |
1834 | | |
1835 | | MSBuildProvider::MSBuildProvider(const int version, std::string global_warnings, std::map<std::string, std::string> project_warnings) |
1836 | | : ProjectProvider(version, global_warnings, project_warnings) { |
1837 | | |
1838 | | } |
1839 | | |
1840 | | const char *MSBuildProvider::getProjectExtension() { |
1841 | | return ".vcxproj"; |
1842 | | } |
1843 | | |
1844 | | const char *MSBuildProvider::getPropertiesExtension() { |
1845 | | return ".props"; |
1846 | | } |
1847 | | |
1848 | | int MSBuildProvider::getVisualStudioVersion() { |
1849 | | return 2010; |
1850 | | } |
1851 | | |
1852 | | #define OUTPUT_CONFIGURATION_MSBUILD(config, platform) \ |
1853 | | project << "\t\t<ProjectConfiguration Include=\"" << config << "|" << platform << "\">\n" \ |
1854 | | "\t\t\t<Configuration>" << config << "</Configuration>\n" \ |
1855 | | "\t\t\t<Platform>" << platform << "</Platform>\n" \ |
1856 | | "\t\t</ProjectConfiguration>\n" |
1857 | | |
1858 | | #define OUTPUT_CONFIGURATION_TYPE_MSBUILD(config) \ |
1859 | | project << "\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='" << config << "'\" Label=\"Configuration\">\n" \ |
1860 | | "\t\t<ConfigurationType>" << (name == "scummvm" ? "Application" : "StaticLibrary") << "</ConfigurationType>\n" \ |
1861 | | "\t</PropertyGroup>\n" |
1862 | | |
1863 | | #define OUTPUT_PROPERTIES_MSBUILD(config, properties) \ |
1864 | | project << "\t<ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='" << config << "'\" Label=\"PropertySheets\">\n" \ |
1865 | | "\t\t<Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n" \ |
1866 | | "\t\t<Import Project=\"" << properties << "\" />\n" \ |
1867 | | "\t</ImportGroup>\n" |
1868 | | |
1869 | | void MSBuildProvider::createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir, |
1870 | | const StringList &includeList, const StringList &excludeList) { |
1871 | | const std::string projectFile = setup.outputDir + '/' + name + getProjectExtension(); |
1872 | | std::ofstream project(projectFile.c_str()); |
1873 | | if (!project) |
1874 | | error("Could not open \"" + projectFile + "\" for writing"); |
1875 | | |
1876 | | project << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
1877 | | "<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n" |
1878 | | "\t<ItemGroup Label=\"ProjectConfigurations\">\n"; |
1879 | | |
1880 | | OUTPUT_CONFIGURATION_MSBUILD("Debug", "Win32"); |
1881 | | OUTPUT_CONFIGURATION_MSBUILD("Debug", "x64"); |
1882 | | OUTPUT_CONFIGURATION_MSBUILD("Release", "Win32"); |
1883 | | OUTPUT_CONFIGURATION_MSBUILD("Release", "x64"); |
1884 | | |
1885 | | project << "\t</ItemGroup>\n"; |
1886 | | |
1887 | | // Project name & Guid |
1888 | | project << "\t<PropertyGroup Label=\"Globals\">\n" |
1889 | | "\t\t<ProjectGuid>" << uuid << "</ProjectGuid>\n" |
1890 | | "\t\t<RootNamespace>" << name << "</RootNamespace>\n" |
1891 | | "\t\t<Keyword>Win32Proj</Keyword>\n" |
1892 | | "\t</PropertyGroup>\n"; |
1893 | | |
1894 | | // Shared configuration |
1895 | | project << "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n"; |
1896 | | |
1897 | | OUTPUT_CONFIGURATION_TYPE_MSBUILD("Release|Win32"); |
1898 | | OUTPUT_CONFIGURATION_TYPE_MSBUILD("Debug|Win32"); |
1899 | | OUTPUT_CONFIGURATION_TYPE_MSBUILD("Release|x64"); |
1900 | | OUTPUT_CONFIGURATION_TYPE_MSBUILD("Debug|x64"); |
1901 | | |
1902 | | project << "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n" |
1903 | | "\t<ImportGroup Label=\"ExtensionSettings\">\n" |
1904 | | "\t</ImportGroup>\n"; |
1905 | | |
1906 | | OUTPUT_PROPERTIES_MSBUILD("Release|Win32", "ScummVM_Release.props"); |
1907 | | OUTPUT_PROPERTIES_MSBUILD("Debug|Win32", "ScummVM_Debug.props"); |
1908 | | OUTPUT_PROPERTIES_MSBUILD("Release|x64", "ScummVM_Release64.props"); |
1909 | | OUTPUT_PROPERTIES_MSBUILD("Debug|x64", "ScummVM_Debug64.props"); |
1910 | | |
1911 | | project << "\t<PropertyGroup Label=\"UserMacros\" />\n"; |
1912 | | |
1913 | | // Project version number |
1914 | | project << "\t<PropertyGroup>\n" |
1915 | | "\t\t<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\n"; |
1916 | | project << "\t</PropertyGroup>\n"; |
1917 | | |
1918 | | // Project-specific settings |
1919 | | outputProjectSettings(project, name, setup, false, true); |
1920 | | outputProjectSettings(project, name, setup, true, true); |
1921 | | outputProjectSettings(project, name, setup, false, false); |
1922 | | outputProjectSettings(project, name, setup, true, false); |
1923 | | |
1924 | | // Files |
1925 | | std::string modulePath; |
1926 | | if (!moduleDir.compare(0, setup.srcDir.size(), setup.srcDir)) { |
1927 | | modulePath = moduleDir.substr(setup.srcDir.size()); |
1928 | | if (!modulePath.empty() && modulePath.at(0) == '/') |
1929 | | modulePath.erase(0, 1); |
1930 | | } |
1931 | | |
1932 | | if (modulePath.size()) |
1933 | | addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix + '/' + modulePath); |
1934 | | else |
1935 | | addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix); |
1936 | | |
1937 | | // Output references for scummvm project |
1938 | | if (name == "scummvm") |
1939 | | writeReferences(project); |
1940 | | |
1941 | | project << "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n" |
1942 | | "\t<ImportGroup Label=\"ExtensionTargets\">\n" |
1943 | | "\t</ImportGroup>\n" |
1944 | | "</Project>\n"; |
1945 | | |
1946 | | // Output filter file if necessary |
1947 | | createFiltersFile(setup, name); |
1948 | | } |
1949 | | |
1950 | | #define OUTPUT_FILTER_MSBUILD(files, action) \ |
1951 | | if (!files.empty()) { \ |
1952 | | filters << "\t<ItemGroup>\n"; \ |
1953 | | for (std::list<FileEntry>::const_iterator entry = files.begin(); entry != files.end(); ++entry) { \ |
1954 | | if ((*entry).filter != "") { \ |
1955 | | filters << "\t\t<" action " Include=\"" << (*entry).path << "\">\n" \ |
1956 | | "\t\t\t<Filter>" << (*entry).filter << "</Filter>\n" \ |
1957 | | "\t\t</" action ">\n"; \ |
1958 | | } else { \ |
1959 | | filters << "\t\t<" action " Include=\"" << (*entry).path << "\" />\n"; \ |
1960 | | } \ |
1961 | | } \ |
1962 | | filters << "\t</ItemGroup>\n"; \ |
1963 | | } |
1964 | | |
1965 | | void MSBuildProvider::createFiltersFile(const BuildSetup &setup, const std::string &name) { |
1966 | | // No filters => no need to create a filter file |
1967 | | if (_filters.empty()) |
1968 | | return; |
1969 | | |
1970 | | // Sort all list alphabetically |
1971 | | _filters.sort(); |
1972 | | _compileFiles.sort(); |
1973 | | _includeFiles.sort(); |
1974 | | _otherFiles.sort(); |
1975 | | _resourceFiles.sort(); |
1976 | | _asmFiles.sort(); |
1977 | | |
1978 | | const std::string filtersFile = setup.outputDir + '/' + name + getProjectExtension() + ".filters"; |
1979 | | std::ofstream filters(filtersFile.c_str()); |
1980 | | if (!filters) |
1981 | | error("Could not open \"" + filtersFile + "\" for writing"); |
1982 | | |
1983 | | filters << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
1984 | | "<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n"; |
1985 | | |
1986 | | // Output the list of filters |
1987 | | filters << "\t<ItemGroup>\n"; |
1988 | | for (std::list<std::string>::iterator filter = _filters.begin(); filter != _filters.end(); ++filter) { |
1989 | | filters << "\t\t<Filter Include=\"" << *filter << "\">\n" |
1990 | | "\t\t\t<UniqueIdentifier>" << createUUID() << "</UniqueIdentifier>\n" |
1991 | | "\t\t</Filter>\n"; |
1992 | | } |
1993 | | filters << "\t</ItemGroup>\n"; |
1994 | | |
1995 | | // Output files |
1996 | | OUTPUT_FILTER_MSBUILD(_compileFiles, "ClCompile") |
1997 | | OUTPUT_FILTER_MSBUILD(_includeFiles, "ClInclude") |
1998 | | OUTPUT_FILTER_MSBUILD(_otherFiles, "None") |
1999 | | OUTPUT_FILTER_MSBUILD(_resourceFiles, "ResourceCompile") |
2000 | | OUTPUT_FILTER_MSBUILD(_asmFiles, "CustomBuild") |
2001 | | |
2002 | | filters << "</Project>"; |
2003 | | } |
2004 | | |
2005 | | void MSBuildProvider::writeReferences(std::ofstream &output) { |
2006 | | output << "\t<ItemGroup>\n"; |
2007 | | |
2008 | | for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) { |
2009 | | if (i->first == "scummvm") |
2010 | | continue; |
2011 | | |
2012 | | output << "\t<ProjectReference Include=\"" << i->first << ".vcxproj\">\n" |
2013 | | "\t\t<Project>{" << i->second << "}</Project>\n" |
2014 | | "\t</ProjectReference>\n"; |
2015 | | } |
2016 | | |
2017 | | output << "\t</ItemGroup>\n"; |
2018 | | } |
2019 | | |
2020 | | void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::string &name, const BuildSetup &setup, bool isRelease, bool isWin32) { |
2021 | | // Check for project-specific warnings: |
2022 | | std::map<std::string, std::string>::iterator warnings = _projectWarnings.find(name); |
2023 | | |
2024 | | // Nothing to add here, move along! |
2025 | | if (name != "scummvm" && name != "tinsel" && warnings == _projectWarnings.end()) |
2026 | | return; |
2027 | | |
2028 | | project << "\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='" << (isRelease ? "Release" : "Debug") << "|" << (isWin32 ? "Win32" : "x64") << "'\">\n" |
2029 | | "\t\t<ClCompile>\n"; |
2030 | | |
2031 | | // Compile configuration |
2032 | | if (name == "scummvm") { |
2033 | | project << "\t\t\t<DisableLanguageExtensions>false</DisableLanguageExtensions>\n"; |
2034 | | } else { |
2035 | | if (name == "tinsel" && !isRelease) |
2036 | | project << "\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\n"; |
2037 | | |
2038 | | if (name == "sword25") |
2039 | | project << "\t\t\t<DisableLanguageExtensions>false</DisableLanguageExtensions>\n"; |
2040 | | |
2041 | | if (warnings != _projectWarnings.end()) |
2042 | | project << "\t\t\t<DisableSpecificWarnings>" << warnings->second << ";%(DisableSpecificWarnings)</DisableSpecificWarnings>\n"; |
2043 | | } |
2044 | | |
2045 | | project << "\t\t</ClCompile>\n"; |
2046 | | |
2047 | | // Link configuration for scummvm project |
2048 | | if (name == "scummvm") { |
2049 | | std::string libraries; |
2050 | | |
2051 | | for (StringList::const_iterator i = setup.libraries.begin(); i != setup.libraries.end(); ++i) |
2052 | | libraries += *i + ';'; |
2053 | | |
2054 | | project << "\t\t<Link>\n" |
2055 | | "\t\t\t<OutputFile>$(OutDir)scummvm.exe</OutputFile>\n" |
2056 | | "\t\t\t<AdditionalDependencies>" << libraries << "%(AdditionalDependencies)</AdditionalDependencies>\n" |
2057 | | "\t\t</Link>\n"; |
2058 | | } |
2059 | | |
2060 | | project << "\t</ItemDefinitionGroup>\n"; |
2061 | | } |
2062 | | |
2063 | | void MSBuildProvider::outputGlobalPropFile(std::ofstream &properties, int bits, const std::string &defines, const std::string &prefix, bool isWin32) { |
2064 | | properties << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
2065 | | "<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n" |
2066 | | "\t<PropertyGroup>\n" |
2067 | | "\t\t<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\n" |
2068 | | "\t\t<_PropertySheetDisplayName>ScummVM_Global</_PropertySheetDisplayName>\n" |
2069 | | "\t\t<ExecutablePath>$(SCUMMVM_LIBS)\\bin;$(ExecutablePath)</ExecutablePath>\n" |
2070 | | "\t\t<LibraryPath>$(SCUMMVM_LIBS)\\libs\\" << (isWin32 ? "x86" : "x64") << ";$(LibraryPath)</LibraryPath>\n" |
2071 | | "\t\t<IncludePath>$(SCUMMVM_LIBS)\\include;$(IncludePath)</IncludePath>\n" |
2072 | | "\t\t<OutDir>$(Configuration)" << bits << "\\</OutDir>\n" |
2073 | | "\t\t<IntDir>$(Configuration)" << bits << "/$(ProjectName)\\</IntDir>\n" |
2074 | | "\t</PropertyGroup>\n" |
2075 | | "\t<ItemDefinitionGroup>\n" |
2076 | | "\t\t<ClCompile>\n" |
2077 | | "\t\t\t<DisableLanguageExtensions>true</DisableLanguageExtensions>\n" |
2078 | | "\t\t\t<DisableSpecificWarnings>" << _globalWarnings << ";%(DisableSpecificWarnings)</DisableSpecificWarnings>\n" |
2079 | | "\t\t\t<AdditionalIncludeDirectories>" << prefix << ";" << prefix << "\\engines;$(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n" |
2080 | | "\t\t\t<PreprocessorDefinitions>" << defines << ";%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" |
2081 | | "\t\t\t<ExceptionHandling>\n" |
2082 | | "\t\t\t</ExceptionHandling>\n" |
2083 | | "\t\t\t<RuntimeTypeInfo>false</RuntimeTypeInfo>\n" |
2084 | | "\t\t\t<WarningLevel>Level4</WarningLevel>\n" |
2085 | | "\t\t\t<TreatWarningAsError>false</TreatWarningAsError>\n" |
2086 | | "\t\t\t<CompileAs>Default</CompileAs>\n" |
2087 | | "\t\t</ClCompile>\n" |
2088 | | "\t\t<Link>\n" |
2089 | | "\t\t\t<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\n" |
2090 | | "\t\t\t<SubSystem>Console</SubSystem>\n" |
2091 | | "\t\t\t<EntryPointSymbol>WinMainCRTStartup</EntryPointSymbol>\n" |
2092 | | "\t\t</Link>\n" |
2093 | | "\t\t<ResourceCompile>\n" |
2094 | | "\t\t\t<PreprocessorDefinitions>HAS_INCLUDE_SET;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" |
2095 | | "\t\t\t<AdditionalIncludeDirectories>" << prefix << ";%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n" |
2096 | | "\t\t</ResourceCompile>\n" |
2097 | | "\t</ItemDefinitionGroup>\n" |
2098 | | "</Project>\n"; |
2099 | | |
2100 | | properties.flush(); |
2101 | | } |
2102 | | |
2103 | | void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, bool isWin32) { |
2104 | | const std::string outputType = (isRelease ? "Release" : "Debug"); |
2105 | | const std::string outputBitness = (isWin32 ? "32" : "64"); |
2106 | | |
2107 | | std::ofstream properties((setup.outputDir + '/' + "ScummVM_" + outputType + (isWin32 ? "" : "64") + getPropertiesExtension()).c_str()); |
2108 | | if (!properties) |
2109 | | error("Could not open \"" + setup.outputDir + '/' + "ScummVM_" + outputType + (isWin32 ? "" : "64") + getPropertiesExtension() + "\" for writing"); |
2110 | | |
2111 | | properties << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
2112 | | "<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n" |
2113 | | "\t<ImportGroup Label=\"PropertySheets\">\n" |
2114 | | "\t\t<Import Project=\"ScummVM_Global" << (isWin32 ? "" : "64") << ".props\" />\n" |
2115 | | "\t</ImportGroup>\n" |
2116 | | "\t<PropertyGroup>\n" |
2117 | | "\t\t<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\n" |
2118 | | "\t\t<_PropertySheetDisplayName>ScummVM_" << outputType << outputBitness << "</_PropertySheetDisplayName>\n" |
2119 | | "\t\t<LinkIncremental>" << (isRelease ? "false" : "true") << "</LinkIncremental>\n" |
2120 | | "\t</PropertyGroup>\n" |
2121 | | "\t<ItemDefinitionGroup>\n" |
2122 | | "\t\t<ClCompile>\n"; |
2123 | | |
2124 | | |
2125 | | |
2126 | | if (isRelease) { |
2127 | | properties << "\t\t\t<IntrinsicFunctions>true</IntrinsicFunctions>\n" |
2128 | | "\t\t\t<WholeProgramOptimization>true</WholeProgramOptimization>\n" |
2129 | | "\t\t\t<PreprocessorDefinitions>WIN32;RELEASE_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" |
2130 | | "\t\t\t<StringPooling>true</StringPooling>\n" |
2131 | | "\t\t\t<BufferSecurityCheck>false</BufferSecurityCheck>\n" |
2132 | | "\t\t\t<DebugInformationFormat></DebugInformationFormat>\n" |
2133 | | "\t\t</ClCompile>\n" |
2134 | | "\t\t<Link>\n" |
2135 | | "\t\t\t<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\n" |
2136 | | "\t\t\t<SetChecksum>true</SetChecksum>\n"; |
2137 | | } else { |
2138 | | properties << "\t\t\t<Optimization>Disabled</Optimization>\n" |
2139 | | "\t\t\t<PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" |
2140 | | "\t\t\t<MinimalRebuild>true</MinimalRebuild>\n" |
2141 | | "\t\t\t<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\n" |
2142 | | "\t\t\t<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n" |
2143 | | "\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\n" |
2144 | | "\t\t\t<TreatWarningAsError>false</TreatWarningAsError>\n" |
2145 | | "\t\t\t<DebugInformationFormat>" << (isWin32 ? "EditAndContinue" : "ProgramDatabase") << "</DebugInformationFormat>\n" // For x64 format Edit and continue is not supported, thus we default to Program Database |
2146 | | "\t\t</ClCompile>\n" |
2147 | | "\t\t<Link>\n" |
2148 | | "\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\n" |
2149 | | "\t\t\t<IgnoreSpecificDefaultLibraries>libcmt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\n"; |
2150 | | } |
2151 | | |
2152 | | properties << "\t\t</Link>\n" |
2153 | | "\t</ItemDefinitionGroup>\n" |
2154 | | "</Project>\n"; |
2155 | | |
2156 | | properties.flush(); |
2157 | | properties.close(); |
2158 | | } |
2159 | | |
2160 | | #define OUTPUT_NASM_COMMAND_MSBUILD(config) \ |
2161 | | projectFile << "\t\t\t<Command Condition=\"'$(Configuration)|$(Platform)'=='" << config << "|Win32'\">nasm.exe -f win32 -g -o \"$(IntDir)" << (isDuplicate ? (*entry).prefix : "") << "%(Filename).obj\" \"%(FullPath)\"</Command>\n" \ |
2162 | | "\t\t\t<Outputs Condition=\"'$(Configuration)|$(Platform)'=='" << config << "|Win32'\">$(IntDir)" << (isDuplicate ? (*entry).prefix : "") << "%(Filename).obj;%(Outputs)</Outputs>\n"; |
2163 | | |
2164 | | #define OUPUT_OBJECT_FILENAME_MSBUILD(config, platform, prefix) \ |
2165 | | projectFile << "\t\t<ObjectFileName Condition=\"'$(Configuration)|$(Platform)'=='" << config << "|" << platform << "'\">$(IntDir)" << prefix << "%(Filename).obj</ObjectFileName>\n" \ |
2166 | | "\t\t<XMLDocumentationFileName Condition=\"'$(Configuration)|$(Platform)'=='" << config << "|" << platform << "'\">$(IntDir)" << prefix << "%(Filename).xdc</XMLDocumentationFileName>\n"; |
2167 | | |
2168 | | #define OUPUT_FILES_MSBUILD(files, action) \ |
2169 | | if (!files.empty()) { \ |
2170 | | projectFile << "\t<ItemGroup>\n"; \ |
2171 | | for (std::list<FileEntry>::const_iterator entry = files.begin(); entry != files.end(); ++entry) { \ |
2172 | | projectFile << "\t\t<" action " Include=\"" << (*entry).path << "\" />\n"; \ |
2173 | | } \ |
2174 | | projectFile << "\t</ItemGroup>\n"; \ |
2175 | | } |
2176 | | |
2177 | | void MSBuildProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int, const StringList &duplicate, |
2178 | | const std::string &objPrefix, const std::string &filePrefix) { |
2179 | | // Reset lists |
2180 | | _filters.clear(); |
2181 | | _compileFiles.clear(); |
2182 | | _includeFiles.clear(); |
2183 | | _otherFiles.clear(); |
2184 | | _resourceFiles.clear(); |
2185 | | _asmFiles.clear(); |
2186 | | |
2187 | | // Compute the list of files |
2188 | | _filters.push_back(""); // init filters |
2189 | | computeFileList(dir, duplicate, objPrefix, filePrefix); |
2190 | | _filters.pop_back(); // remove last empty filter |
2191 | | |
2192 | | // Output compile files |
2193 | | if (!_compileFiles.empty()) { |
2194 | | projectFile << "\t<ItemGroup>\n"; |
2195 | | for (std::list<FileEntry>::const_iterator entry = _compileFiles.begin(); entry != _compileFiles.end(); ++entry) { |
2196 | | const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), (*entry).name + ".o") != duplicate.end()); |
2197 | | |
2198 | | // Deal with duplicated file names |
2199 | | if (isDuplicate) { |
2200 | | projectFile << "\t\t<ClCompile Include=\"" << (*entry).path << "\">\n"; |
2201 | | OUPUT_OBJECT_FILENAME_MSBUILD("Debug", "Win32", (*entry).prefix) |
2202 | | OUPUT_OBJECT_FILENAME_MSBUILD("Debug", "x64", (*entry).prefix) |
2203 | | OUPUT_OBJECT_FILENAME_MSBUILD("Release", "Win32", (*entry).prefix) |
2204 | | OUPUT_OBJECT_FILENAME_MSBUILD("Release", "x64", (*entry).prefix) |
2205 | | projectFile << "\t\t</ClCompile>\n"; |
2206 | | } else { |
2207 | | projectFile << "\t\t<ClCompile Include=\"" << (*entry).path << "\" />\n"; |
2208 | | } |
2209 | | } |
2210 | | projectFile << "\t</ItemGroup>\n"; |
2211 | | } |
2212 | | |
2213 | | // Output include, other and resource files |
2214 | | OUPUT_FILES_MSBUILD(_includeFiles, "ClInclude") |
2215 | | OUPUT_FILES_MSBUILD(_otherFiles, "None") |
2216 | | OUPUT_FILES_MSBUILD(_resourceFiles, "ResourceCompile") |
2217 | | |
2218 | | // Output asm files |
2219 | | if (!_asmFiles.empty()) { |
2220 | | projectFile << "\t<ItemGroup>\n"; |
2221 | | for (std::list<FileEntry>::const_iterator entry = _asmFiles.begin(); entry != _asmFiles.end(); ++entry) { |
2222 | | |
2223 | | const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), (*entry).name + ".o") != duplicate.end()); |
2224 | | |
2225 | | projectFile << "\t\t<CustomBuild Include=\"" << (*entry).path << "\">\n" |
2226 | | "\t\t\t<FileType>Document</FileType>\n"; |
2227 | | |
2228 | | OUTPUT_NASM_COMMAND_MSBUILD("Debug") |
2229 | | OUTPUT_NASM_COMMAND_MSBUILD("Release") |
2230 | | |
2231 | | projectFile << "\t\t</CustomBuild>\n"; |
2232 | | } |
2233 | | projectFile << "\t</ItemGroup>\n"; |
2234 | | } |
2235 | | } |
2236 | | |
2237 | | void MSBuildProvider::computeFileList(const FileNode &dir, const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix) { |
2238 | | for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) { |
2239 | | const FileNode *node = *i; |
2240 | | |
2241 | | if (!node->children.empty()) { |
2242 | | // Update filter |
2243 | | std::string _currentFilter = _filters.back(); |
2244 | | _filters.back().append((_filters.back() == "" ? "" : "\\") + node->name); |
2245 | | |
2246 | | computeFileList(*node, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/'); |
2247 | | |
2248 | | // Reset filter |
2249 | | _filters.push_back(_currentFilter); |
2250 | | } else { |
2251 | | // Filter files by extension |
2252 | | std::string name, ext; |
2253 | | splitFilename(node->name, name, ext); |
2254 | | |
2255 | | FileEntry entry; |
2256 | | entry.name = name; |
2257 | | entry.path = convertPathToWin(filePrefix + node->name); |
2258 | | entry.filter = _filters.back(); |
2259 | | entry.prefix = objPrefix; |
2260 | | |
2261 | | if (ext == "cpp" || ext == "c") |
2262 | | _compileFiles.push_back(entry); |
2263 | | else if (ext == "h") |
2264 | | _includeFiles.push_back(entry); |
2265 | | else if (ext == "rc") |
2266 | | _resourceFiles.push_back(entry); |
2267 | | else if (ext == "asm") |
2268 | | _asmFiles.push_back(entry); |
2269 | | else |
2270 | | _otherFiles.push_back(entry); |
2271 | | } |
2272 | | } |
2273 | | } |
2274 | | |
2275 | | } // End of anonymous namespace |
2276 | | |
2277 | | void error(const std::string &message) { |
2278 | | std::cerr << "ERROR: " << message << "!" << std::endl; |
2279 | | std::exit(-1); |
2280 | | } |
2281 | | |