buildsystem, epj2make, ide: support Emscripten compiler.
[sdk] / epj2make / epj2make.ec
1 #ifdef ECERE_STATIC
2 public import static "ecere"
3 #else
4 public import "ecere"
5 #endif
6
7 import "Project"
8
9 #if defined(__WIN32__)
10 define pathListSep = ";";
11 #else
12 define pathListSep = ":";
13 #endif
14
15 #if defined(_DEBUG) && defined(__WIN32__)
16 extern int getch(void);
17 #endif
18
19 void ParseDirList(char * string, Container<String> list)
20 {
21    int c;
22    char * tokens[256];
23    int numTokens = TokenizeWith(string, sizeof(tokens) / sizeof(byte *), tokens, ";", false);
24    list.Free();
25    for(c = 0; c < numTokens; c++)
26       list.Add(CopyString(tokens[c]));
27 }
28
29 // NOTE: FileMonitors are not to be used by an Application class. globalSystem is not set up properly otherwise.
30 // Mantis ticket #545 was filed regarding this.
31 class epj2makeApp : GuiApplication
32 {
33    void Main()
34    {
35       int c;
36       bool valid = true;
37       char * configName = null;
38       char * epjPath = null;
39       char * makePath = null;
40       /*
41       char ** argv = null;
42       int argc = 0;
43       */
44
45       Project project = null;
46
47       CompilerConfig optionsCompiler { };
48
49       bool noGlobalSettings = false;
50       bool noResources = false;
51       bool noWarnings = false;
52       const char * overrideObjDir = null;
53       const char * includemkPath = null;
54
55       /*
56       for(c = 0; c < this.argc; c++)
57       {
58          char * arg = this.argv[c];
59          int argLen = strlen(arg);
60
61          argv = renew argv char *[argc + 1];
62          argv[argc] = new char[argLen + 1];
63          strcpy(argv[argc], arg);
64
65          while(argv[argc][argLen - 1] == '\\' && c < this.argc-1)
66          {
67             int len;
68
69             c++;
70             arg = this.argv[c];
71             len = strlen(arg);
72             argv[argc] = renew argv[argc] char[argLen + len + 1];
73
74             argv[argc][argLen - 1] = ' ';
75             strcpy(argv[argc] + argLen, arg);
76             argLen += len;
77          }
78          argc++;
79       }
80       */
81
82       for(c = 1; c < argc; c++)
83       {
84          const char * arg = argv[c];
85          if(arg[0] == '-')
86          {
87             if(!strcmpi(arg+1, "make"))
88             {
89                if(++c < argc)
90                   optionsCompiler.makeCommand = argv[c];
91                else
92                   valid = false;
93             }
94             else if(!strcmpi(arg+1, "cpp"))
95             {
96                if(++c < argc)
97                   optionsCompiler.cppCommand = argv[c];
98                else
99                   valid = false;
100             }
101             else if(!strcmpi(arg+1, "cc"))
102             {
103                if(++c < argc)
104                   optionsCompiler.ccCommand = argv[c];
105                else
106                   valid = false;
107             }
108             else if(!strcmpi(arg+1, "ecp"))
109             {
110                if(++c < argc)
111                   optionsCompiler.ecpCommand = argv[c];
112                else
113                   valid = false;
114             }
115             else if(!strcmpi(arg+1, "ecc"))
116             {
117                if(++c < argc)
118                   optionsCompiler.eccCommand = argv[c];
119                else
120                   valid = false;
121             }
122             else if(!strcmpi(arg+1, "ecs"))
123             {
124                if(++c < argc)
125                   optionsCompiler.ecsCommand = argv[c];
126                else
127                   valid = false;
128             }
129             else if(!strcmpi(arg+1, "ear"))
130             {
131                if(++c < argc)
132                   optionsCompiler.earCommand = argv[c];
133                else
134                   valid = false;
135             }
136             else if(!strcmpi(arg+1, "noglobalsettings"))
137             {
138                noGlobalSettings = true;
139             }
140             else if(!strcmpi(arg+1, "noresources"))
141             {
142                noResources = true;
143             }
144             else if(!strcmpi(arg+1, "includemk"))
145             {
146                if(++c < argc)
147                   includemkPath = argv[c];
148                else
149                   valid = false;
150             }
151             else if(arg[1] == 'w' && !arg[2])
152             {
153                noWarnings = true;
154             }
155             else if(arg[1] == 'c' && !arg[2])
156             {
157                if(++c < argc)
158                {
159                   int argLen = strlen(argv[c]);
160                   configName = new char[argLen + 1];
161                   strcpy(configName, argv[c]);
162                }
163                else
164                   valid = false;
165             }
166             else if(arg[1] == 't' && !arg[2])
167             {
168                // Obsolete target platform option
169                if(++c < argc);
170                else
171                   valid = false;
172             }
173             else if(arg[1] == 'o')
174             {
175                if(++c < argc)
176                {
177                   int argLen = strlen(argv[c]);
178                   makePath = new char[argLen + 1];
179                   strcpy(makePath, argv[c]);
180                }
181                else
182                   valid = false;
183             }
184             else if(arg[1] == 'i')
185             {
186                if(++c < argc)
187                   ParseDirList((char *)argv[c], optionsCompiler.includeDirs);
188                else
189                   valid = false;
190             }
191             else if(arg[1] == 'l')
192             {
193                if(++c < argc)
194                   ParseDirList((char *)argv[c], optionsCompiler.libraryDirs);
195                else
196                   valid = false;
197             }
198             else if(arg[1] == 'd' && !arg[2])
199             {
200                if(++c < argc)
201                   overrideObjDir = argv[c];
202                else
203                   valid = false;
204             }
205             else
206             {
207                valid = false;
208                printf($"invalid option: %s\n", arg);
209             }
210          }
211          else
212          {
213             if(!epjPath)
214             {
215                int argLen = strlen(arg);
216                epjPath = new char[argLen + 1];
217                strcpy(epjPath, arg);
218                c++;
219             }
220             else
221                valid = false;
222          }
223       }
224       if(!epjPath)
225          valid = false;
226
227       if(!valid)
228       {
229          printf("%s", $"Syntax:\n");
230          printf("%s", $"   epj2make [-t <target platform>] [-c <configuration>] [toolchain] [directories] [options] [-o <output>] <input>\n");
231          printf("%s", $"      toolchain:\n");
232          printf("%s", $"         [-make <make tool>]\n");
233          printf("%s", $"         [-cpp <c preprocessor>]\n");
234          printf("%s", $"         [-cc <c compiler>]\n");
235          printf("%s", $"         [-ecp <eC preprocessor>]\n");
236          printf("%s", $"         [-ecc <eC compiler>]\n");
237          printf("%s", $"         [-ecs <eC symbol generator>]\n");
238          printf("%s", $"         [-ear <Ecere Archiver>]\n");
239          printf("%s", $"      directories:\n");
240          printf("%s", $"         [-i <include dir[;inc dir[...]]>]\n");
241          printf("%s", $"         [-l <library dir[;lib dir[...]]>]\n");
242          printf("%s", $"      options:\n");
243          printf("%s", $"         [-noglobalsettings]\n");
244          printf("%s", $"         [-noresources]\n");
245          printf("%s", $"         [-d <intermediate objects directory>]\n");
246          printf("%s", $"         [-includemk <crossplatform.mk path>]\n");
247       }
248       else
249       {
250          if(FileExists(epjPath).isFile)
251          {
252             char extension[MAX_EXTENSION] = "";
253             GetExtension(epjPath, extension);
254             strlwr(extension);
255             if(!strcmp(extension, ProjectExtension))
256             {
257                if(noGlobalSettings)
258                {
259                   defaultCompiler = MakeDefaultCompiler("Default", true);
260                   delete ideSettings;
261                }
262                else
263                {
264                   const char * compiler = getenv("COMPILER");
265                   if(!compiler) compiler = "Default";
266                   settingsContainer.Load();
267                   //incref ideSettings;
268                   delete settingsContainer;
269
270       // TODO: Command line option to choose between the two
271       // or a command line option to not use global settings
272       //defaultCompiler = MakeDefaultCompiler();
273                   defaultCompiler = ideSettings.GetCompilerConfig(compiler);
274       // possible TODO: use the workspace to select the active compiler
275       // TODO: option to specify compiler name when using global settings
276                }
277
278                if(optionsCompiler.makeCommand)
279                   defaultCompiler.makeCommand = optionsCompiler.makeCommand;
280                if(optionsCompiler.cppCommand)
281                   defaultCompiler.cppCommand = optionsCompiler.cppCommand;
282                if(optionsCompiler.ccCommand)
283                   defaultCompiler.ccCommand = optionsCompiler.ccCommand;
284                if(optionsCompiler.ecpCommand)
285                   defaultCompiler.ecpCommand = optionsCompiler.ecpCommand;
286                if(optionsCompiler.eccCommand)
287                   defaultCompiler.eccCommand = optionsCompiler.eccCommand;
288                if(optionsCompiler.ecsCommand)
289                   defaultCompiler.ecsCommand = optionsCompiler.ecsCommand;
290                if(optionsCompiler.earCommand)
291                   defaultCompiler.earCommand = optionsCompiler.earCommand;
292                // TODO: Pass/combine/override the include and library dirs
293                for(dir : optionsCompiler.includeDirs)
294                   defaultCompiler.includeDirs.Add(dir);
295                for(dir : optionsCompiler.libraryDirs)
296                   defaultCompiler.libraryDirs.Add(dir);
297                delete optionsCompiler;
298
299                project = LoadProject(epjPath, null);
300                if(project)
301                {
302                   ProjectConfig defaultConfig = null;
303                   if(configName)
304                   {
305                      valid = false;
306                      for(config : project.configurations)
307                      {
308                         if(!strcmpi(configName, config.name))
309                         {
310                            project.config = config;
311                            valid = true;
312                            break;
313                         }
314                      }
315                      if(!valid)
316                         printf($"Error: Project configuration (%s) was not found.\n", configName);
317                   }
318                   else
319                   {
320                      ProjectConfig releaseConfig = null;
321                      for(config : project.configurations)
322                      {
323                         if(!strcmpi(config.name, "Release"))
324                         {
325                            releaseConfig = config;
326                            break;
327                         }
328                      }
329                      if(!releaseConfig && project.configurations.count)
330                      {
331                         releaseConfig = project.configurations[0];
332                         printf($"Notice: Project configuration (%s) will be used.\n", releaseConfig.name);
333                      }
334
335                      if(releaseConfig)
336                      {
337                         project.config = releaseConfig;
338                         if(overrideObjDir)
339                         {
340                            delete releaseConfig.options.targetDirectory;
341                            releaseConfig.options.targetDirectory = CopyString(overrideObjDir);
342                            delete releaseConfig.options.objectsDirectory;
343                            releaseConfig.options.objectsDirectory = CopyString(overrideObjDir);
344                         }
345                         if(noWarnings)
346                            releaseConfig.options.warnings = none;
347                      }
348                      else if(overrideObjDir)
349                      {
350                         delete project.options.targetDirectory;
351                         project.options.targetDirectory = CopyString(overrideObjDir);
352                         delete project.options.objectsDirectory;
353                         project.options.objectsDirectory = CopyString(overrideObjDir);
354                      }
355                      if(noWarnings)
356                         project.options.warnings = none;
357                   }
358                   if(valid)
359                   {
360                      project.GenerateCompilerCf(defaultCompiler, project.topNode.ContainsFilesWithExtension("ec", project.config));
361                      project.GenerateCrossPlatformMk(null);
362                      if(project.GenerateMakefile(makePath, noResources, includemkPath, project.config, defaultCompiler.ldCommand))
363                      {
364                         if(makePath)
365                            printf("%s\n", makePath);
366                      }
367                   }
368
369                   if(noGlobalSettings)
370                      delete defaultCompiler;
371
372                   delete defaultConfig;
373                   delete project;
374                }
375                else
376                   printf($"Error: Unable to open project file (%s) due to unknown error.\n", epjPath);
377             }
378          }
379          else
380             printf($"Error: Input file (%s) does not exist.\n", epjPath);
381       }
382
383       //if(optionsCompiler) // how to fix those leaks?
384       delete optionsCompiler;
385
386       delete configName;
387       delete epjPath;
388       delete makePath;
389
390       // Why do we need to free this here?
391       delete defaultCompiler;
392
393       /*
394       for(c = 0; c<argc; c++)
395          delete argv[c];
396       delete argv;
397       */
398
399       // CheckMemory();
400       delete ideSettings;
401 #if defined(_DEBUG) && defined(__WIN32__)
402       getch();
403 #endif
404    }
405 }