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  */
22 
23 #include "config.h"
24 #include "codeblocks.h"
25 
26 #include <fstream>
27 #include <cstring>
28 
29 namespace CreateProjectTool {
30 
CodeBlocksProvider(StringList & global_warnings,std::map<std::string,StringList> & project_warnings,const int version)31 CodeBlocksProvider::CodeBlocksProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version)
32 	: ProjectProvider(global_warnings, project_warnings, version) {
33 }
34 
createWorkspace(const BuildSetup & setup)35 void CodeBlocksProvider::createWorkspace(const BuildSetup &setup) {
36 	std::ofstream workspace((setup.outputDir + '/' + setup.projectName + ".workspace").c_str());
37 	if (!workspace)
38 		error("Could not open \"" + setup.outputDir + '/' + setup.projectName + ".workspace\" for writing");
39 
40 	workspace << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n"
41 	             "<CodeBlocks_workspace_file>\n";
42 
43 	workspace << "\t<Workspace title=\"" << setup.projectDescription << "\">\n";
44 
45 	writeReferences(setup, workspace);
46 
47 	// Note we assume that the UUID map only includes UUIDs for enabled engines!
48 	for (UUIDMap::const_iterator i = _engineUuidMap.begin(); i != _engineUuidMap.end(); ++i) {
49 		workspace << "\t\t<Project filename=\"" << i->first << ".cbp\" />\n";
50 	}
51 
52 	workspace << "\t</Workspace>\n"
53 	             "</CodeBlocks_workspace_file>";
54 }
55 
getFeatureLibraries(const BuildSetup & setup)56 StringList getFeatureLibraries(const BuildSetup &setup) {
57 	StringList libraries;
58 
59 	for (FeatureList::const_iterator i = setup.features.begin(); i != setup.features.end(); ++i) {
60 		if (i->enable && i->library) {
61 			std::string libname;
62 			if (!std::strcmp(i->name, "libz") || !std::strcmp(i->name, "libcurl")) {
63 				libname = i->name;
64 			} else if (!std::strcmp(i->name, "vorbis")) {
65 				libname = "libvorbis";
66 				libraries.push_back("libvorbisfile");
67 			} else if (!std::strcmp(i->name, "png")) {
68 				libname = "libpng16";
69 			} else if (!std::strcmp(i->name, "sdlnet")) {
70 				if (setup.useSDL2) {
71 					libname = "libSDL2_net";
72 				} else {
73 					libname = "libSDL_net";
74 				}
75 				libraries.push_back("iphlpapi");
76 			} else {
77 				libname = "lib";
78 				libname += i->name;
79 			}
80 			libraries.push_back(libname);
81 		}
82 	}
83 
84 	if (setup.useSDL2) {
85 		libraries.push_back("libSDL2");
86 	} else {
87 		libraries.push_back("libSDL");
88 	}
89 
90 	// Win32 libraries
91 	libraries.push_back("ole32");
92 	libraries.push_back("uuid");
93 	libraries.push_back("winmm");
94 
95 	return libraries;
96 }
97 
createProjectFile(const std::string & name,const std::string &,const BuildSetup & setup,const std::string & moduleDir,const StringList & includeList,const StringList & excludeList)98 void CodeBlocksProvider::createProjectFile(const std::string &name, const std::string &, const BuildSetup &setup, const std::string &moduleDir,
99 										   const StringList &includeList, const StringList &excludeList) {
100 
101 	const std::string projectFile = setup.outputDir + '/' + name + getProjectExtension();
102 	std::ofstream project(projectFile.c_str());
103 	if (!project)
104 		error("Could not open \"" + projectFile + "\" for writing");
105 
106 	project << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n"
107 	           "<CodeBlocks_project_file>\n"
108 	           "\t<FileVersion major=\"1\" minor=\"6\" />\n"
109 	           "\t<Project>\n"
110 	           "\t\t<Option title=\"" << name << "\" />\n"
111 	           "\t\t<Option pch_mode=\"2\" />\n"
112 	           "\t\t<Option compiler=\"gcc\" />\n"
113 	           "\t\t<Build>\n";
114 
115 	if (name == setup.projectName) {
116 		StringList libraries = getFeatureLibraries(setup);
117 
118 		std::string deps;
119 		for (StringList::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
120 			deps += (*i) + ".a;";
121 
122 		project << "\t\t\t<Target title=\"default\">\n"
123 		           "\t\t\t\t<Option output=\"" << setup.projectName << "\\" << setup.projectName << "\" prefix_auto=\"1\" extension_auto=\"1\" />\n"
124 		           "\t\t\t\t<Option object_output=\"" << setup.projectName << "\" />\n"
125 		           "\t\t\t\t<Option external_deps=\"" << deps /* + list of engines engines\name\name.a */ << "\" />\n"
126 		           "\t\t\t\t<Option type=\"1\" />\n"
127 		           "\t\t\t\t<Option compiler=\"gcc\" />\n"
128 		           "\t\t\t\t<Option parameters=\"-d 8 --debugflags=parser\" />\n"
129 		           "\t\t\t\t<Option projectIncludeDirsRelation=\"2\" />\n";
130 
131 		//////////////////////////////////////////////////////////////////////////
132 		// Compiler
133 		project << "\t\t\t\t<Compiler>\n";
134 
135 		writeWarnings(name, project);
136 		writeDefines(setup.defines, project);
137 
138 		for (StringList::const_iterator i = setup.includeDirs.begin(); i != setup.includeDirs.end(); ++i)
139 			project << "\t\t\t\t\t<Add directory=\"" << convertPathToWin(*i) << "\" />\n";
140 
141 		project << "\t\t\t\t\t<Add directory=\"$(" << LIBS_DEFINE << ")include\" />\n"
142 		           "\t\t\t\t\t<Add directory=\"$(" << LIBS_DEFINE << ")include\\SDL\" />\n"
143 		           "\t\t\t\t\t<Add directory=\"..\\..\\engines\" />\n"
144 		           "\t\t\t\t\t<Add directory=\"..\\..\\common\" />\n"
145 		           "\t\t\t\t\t<Add directory=\"..\\..\" />\n"
146 		           "\t\t\t\t\t<Add directory=\".\\\" />\n"
147 		           "\t\t\t\t</Compiler>\n";
148 
149 		//////////////////////////////////////////////////////////////////////////
150 		// Linker
151 		project << "\t\t\t\t<Linker>\n";
152 
153 		for (StringList::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
154 			project << "\t\t\t\t\t<Add library=\"" << (*i) << "\" />\n";
155 
156 		for (UUIDMap::const_iterator i = _engineUuidMap.begin(); i != _engineUuidMap.end(); ++i) {
157 			project << "\t\t\t\t\t<Add library=\"" << setup.projectName << "\\engines\\" << i->first << "\\lib" << i->first << ".a\" />\n";
158 		}
159 
160 		for (StringList::const_iterator i = setup.libraryDirs.begin(); i != setup.libraryDirs.end(); ++i)
161 			project << "\t\t\t\t\t<Add directory=\"" << convertPathToWin(*i) << "\" />\n";
162 
163 		project << "\t\t\t\t\t<Add directory=\"$(" << LIBS_DEFINE << ")lib\\mingw\" />\n"
164 		           "\t\t\t\t\t<Add directory=\"$(" << LIBS_DEFINE << ")lib\" />\n"
165 		           "\t\t\t\t</Linker>\n";
166 
167 		//////////////////////////////////////////////////////////////////////////
168 		// Resource compiler
169 		project << "\t\t\t\t<ResourceCompiler>\n"
170 		           "\t\t\t\t\t<Add directory=\"..\\..\\dists\" />\n"
171 		           "\t\t\t\t\t<Add directory=\"..\\..\\..\\" << setup.projectName << "\" />\n"
172 		           "\t\t\t\t</ResourceCompiler>\n"
173 		           "\t\t\t</Target>\n"
174 		           "\t\t</Build>\n";
175 
176 
177 
178 	} else {
179 		project << "\t\t\t<Target title=\"default\">\n"
180 		           "\t\t\t\t<Option output=\"" << setup.projectName << "\\engines\\" << name << "\\lib" << name << "\" prefix_auto=\"1\" extension_auto=\"1\" />\n"
181 		           "\t\t\t\t<Option working_dir=\"\" />\n"
182 		           "\t\t\t\t<Option object_output=\"" << setup.projectName << "\" />\n"
183 		           "\t\t\t\t<Option type=\"2\" />\n"
184 		           "\t\t\t\t<Option compiler=\"gcc\" />\n"
185 		           "\t\t\t\t<Option createDefFile=\"1\" />\n"
186 		           "\t\t\t\t<Compiler>\n";
187 
188 		writeWarnings(name, project);
189 		writeDefines(setup.defines, project);
190 
191 		project << "\t\t\t\t\t<Add option=\"-g\" />\n"
192 		           "\t\t\t\t\t<Add directory=\"..\\..\\engines\" />\n"
193 		           "\t\t\t\t\t<Add directory=\"..\\..\\..\\" << setup.projectName << "\" />\n";
194 
195 		// Sword2.5 engine needs theora and vorbis includes
196 		if (name == "sword25")
197 			project << "\t\t\t\t\t<Add directory=\"$(" << LIBS_DEFINE << ")include\" />\n";
198 
199 		project << "\t\t\t\t</Compiler>\n"
200 		           "\t\t\t</Target>\n"
201 		           "\t\t</Build>\n";
202 	}
203 
204 	std::string modulePath;
205 	if (!moduleDir.compare(0, setup.srcDir.size(), setup.srcDir)) {
206 		modulePath = moduleDir.substr(setup.srcDir.size());
207 		if (!modulePath.empty() && modulePath.at(0) == '/')
208 			modulePath.erase(0, 1);
209 	}
210 
211 	if (!modulePath.empty())
212 		addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix + '/' + modulePath);
213 	else
214 		addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix);
215 
216 
217 	project << "\t\t<Extensions>\n"
218 	           "\t\t\t<code_completion />\n"
219 	           "\t\t\t<debugger />\n"
220 	           "\t\t</Extensions>\n"
221 	           "\t</Project>\n"
222 	           "</CodeBlocks_project_file>";
223 
224 }
225 
addResourceFiles(const BuildSetup & setup,StringList & includeList,StringList & excludeList)226 void CodeBlocksProvider::addResourceFiles(const BuildSetup &setup, StringList &includeList, StringList &excludeList) {
227 	includeList.push_back(setup.srcDir + "/icons/" + setup.projectName + ".ico");
228 	includeList.push_back(setup.srcDir + "/dists/" + setup.projectName + ".rc");
229 }
230 
writeWarnings(const std::string & name,std::ofstream & output) const231 void CodeBlocksProvider::writeWarnings(const std::string &name, std::ofstream &output) const {
232 
233 	// Global warnings
234 	for (StringList::const_iterator i = _globalWarnings.begin(); i != _globalWarnings.end(); ++i)
235 		output << "\t\t\t\t\t<Add option=\"" << *i << "\" />\n";
236 
237 	// Check for project-specific warnings:
238 	std::map<std::string, StringList>::iterator warningsIterator = _projectWarnings.find(name);
239 	if (warningsIterator != _projectWarnings.end())
240 		for (StringList::const_iterator i = warningsIterator->second.begin(); i != warningsIterator->second.end(); ++i)
241 			output << "\t\t\t\t\t<Add option=\"" << *i << "\" />\n";
242 
243 }
244 
writeDefines(const StringList & defines,std::ofstream & output) const245 void CodeBlocksProvider::writeDefines(const StringList &defines, std::ofstream &output) const {
246 	for (StringList::const_iterator i = defines.begin(); i != defines.end(); ++i)
247 		output << "\t\t\t\t\t<Add option=\"-D" << *i << "\" />\n";
248 }
249 
writeFileListToProject(const FileNode & dir,std::ofstream & projectFile,const int indentation,const std::string & objPrefix,const std::string & filePrefix)250 void CodeBlocksProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation,
251 												const std::string &objPrefix, const std::string &filePrefix) {
252 
253 	for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) {
254 		const FileNode *node = *i;
255 
256 		if (!node->children.empty()) {
257 			writeFileListToProject(*node, projectFile, indentation + 1, objPrefix + node->name + '_', filePrefix + node->name + '/');
258 		} else {
259 			std::string name, ext;
260 			splitFilename(node->name, name, ext);
261 
262 			if (ext == "rc") {
263 				projectFile << "\t\t<Unit filename=\"" << convertPathToWin(filePrefix + node->name) << "\">\n"
264 				               "\t\t\t<Option compilerVar=\"WINDRES\" />\n"
265 				               "\t\t</Unit>\n";
266 			} else if (ext == "asm") {
267 				projectFile << "\t\t<Unit filename=\"" << convertPathToWin(filePrefix + node->name) << "\">\n"
268 				               "\t\t\t<Option compiler=\"gcc\" use=\"1\" buildCommand=\"$(" << LIBS_DEFINE << ")bin/nasm.exe -f win32 -g $file -o $object\" />"
269 				               "\t\t</Unit>\n";
270 			} else {
271 				projectFile << "\t\t<Unit filename=\"" << convertPathToWin(filePrefix + node->name) << "\" />\n";
272 			}
273 		}
274 	}
275 }
276 
writeReferences(const BuildSetup & setup,std::ofstream & output)277 void CodeBlocksProvider::writeReferences(const BuildSetup &setup, std::ofstream &output) {
278 	output << "\t\t<Project filename=\"" << setup.projectName << ".cbp\" active=\"1\">\n";
279 
280 	for (UUIDMap::const_iterator i = _engineUuidMap.begin(); i != _engineUuidMap.end(); ++i) {
281 		output << "\t\t\t<Depends filename=\"" << i->first << ".cbp\" />\n";
282 	}
283 
284 	output << "\t\t</Project>\n";
285 }
286 
getProjectExtension()287 const char *CodeBlocksProvider::getProjectExtension() {
288 	return ".cbp";
289 }
290 
291 
292 } // End of CreateProjectTool namespace
293