43e667bd255b94b2310458c76830a58d4cbc9eba
[platform/upstream/coreclr.git] / src / tools / crossgen / crossgen.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 //
6 // TO DO: we currently use raw printf() for output. Maybe we need to pick up something like ngen's Output() handling
7 // to handle multiple code pages, etc, better.
8
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <io.h>
12
13 #include <windows.h>
14 #include <fxver.h>
15 #include <mscorsvc.h>
16
17 #include "palclr.h"
18
19 #include <sstring.h>
20 #include "ex.h"
21
22 #include "coregen.h"
23 #include "consoleargs.h"
24
25 // Return values from wmain() in case of error
26 enum ReturnValues
27 {
28     FAILURE_RESULT = 1,
29     CLR_INIT_ERROR = -2,
30     ASSEMBLY_NOT_FOUND = -3,
31     INVALID_ARGUMENTS = -4
32 };
33
34 #define NumItems(s) (sizeof(s) / sizeof(s[0]))
35
36 STDAPI CreatePDBWorker(LPCWSTR pwzAssemblyPath, LPCWSTR pwzPlatformAssembliesPaths, LPCWSTR pwzTrustedPlatformAssemblies, LPCWSTR pwzPlatformResourceRoots, LPCWSTR pwzAppPaths, LPCWSTR pwzAppNiPaths, LPCWSTR pwzPdbPath, BOOL fGeneratePDBLinesInfo, LPCWSTR pwzManagedPdbSearchPath, LPCWSTR pwzPlatformWinmdPaths, LPCWSTR pwzDiasymreaderPath);
37 STDAPI NGenWorker(LPCWSTR pwzFilename, DWORD dwFlags, LPCWSTR pwzPlatformAssembliesPaths, LPCWSTR pwzTrustedPlatformAssemblies, LPCWSTR pwzPlatformResourceRoots, LPCWSTR pwzAppPaths, LPCWSTR pwzOutputFilename=NULL, LPCWSTR pwzPlatformWinmdPaths=NULL, ICorSvcLogger *pLogger = NULL, LPCWSTR pwszCLRJITPath = nullptr);
38 void SetSvcLogger(ICorSvcLogger *pCorSvcLogger);
39 void SetMscorlibPath(LPCWSTR wzSystemDirectory);
40
41 /* --------------------------------------------------------------------------- *    
42  * Console stuff
43  * --------------------------------------------------------------------------- */
44
45 void Output(LPCWSTR str)
46 {
47     wprintf(W("%s"), str);
48 }
49
50 void Outputf(LPCWSTR szFormat, ...)
51 {
52     va_list args;
53     va_start(args, szFormat);
54     vfwprintf(stdout, szFormat, args);
55     va_end(args);
56 }
57
58 void OutputErr(LPCWSTR str)
59 {
60     fwprintf(stderr, W("%s"), str);
61 }
62
63 void OutputErrf(LPCWSTR szFormat, ...)
64 {
65     va_list args;
66     va_start(args, szFormat);
67     vfwprintf(stderr, szFormat, args);
68     va_end(args);
69 }
70
71 void ErrorHR(HRESULT hr)
72 {
73     OutputErrf(W("Error: failed to initialize CoreCLR: 0x%08x\n"), hr);
74 }
75
76 void ErrorWin32(DWORD err)
77 {
78     ErrorHR(HRESULT_FROM_WIN32(err));
79 }
80
81 // Some error messages are useless to callers, so make them generic, except in debug builds, where we want as much
82 // information as possible.
83
84 #ifdef _DEBUG
85 #define ERROR_HR(msg,hr)        Outputf(msg, hr)
86 #define ERROR_WIN32(msg,err)    Outputf(msg, err)
87 #else // _DEBUG
88 #define ERROR_HR(msg,hr)        ErrorHR(hr)
89 #define ERROR_WIN32(msg,err)    ErrorWin32(err)
90 #endif // _DEBUG
91
92
93 void PrintLogoHelper()
94 {
95     Output(W("Microsoft (R) CoreCLR Native Image "));
96     Outputf(W("Generator - Version %S\n"), VER_FILEVERSION_STR);
97     Outputf(W("%S\n"), VER_LEGALCOPYRIGHT_LOGO_STR);
98     Output(W("\n"));
99 }
100
101 void PrintUsageHelper()
102 {
103     // Always print the logo when we print the usage, even if they've specified /nologo and we've parsed that already.
104     PrintLogoHelper();
105
106     Output(
107        W("Usage: crossgen [args] <assembly name>\n")
108        W("\n")
109        W("    /? or /help          - Display this screen\n")
110        W("    /nologo              - Prevents displaying the logo\n")
111        W("    /silent              - Do not display completion message\n")
112        W("    @response.rsp        - Process command line arguments from specified\n")
113        W("                           response file\n")
114        W("    /partialtrust        - Assembly will be run in a partial trust domain.\n")
115        W("    /in <file>           - Specifies input filename (optional)\n")
116        W("    /out <file>          - Specifies output filename (optional)\n")
117        W("    /Trusted_Platform_Assemblies <path[") PATH_SEPARATOR_STR_W W("path]>\n")
118        W("                         - List of assemblies treated as trusted platform\n")
119        W("                         - Cannot be used with Platform_Assemblies_Paths\n")
120        W("    /Platform_Resource_Roots <path[") PATH_SEPARATOR_STR_W W("path]>\n")
121        W("                         - List of paths containing localized assembly directories\n")
122        W("    /App_Paths <path[") PATH_SEPARATOR_STR_W W("path]>\n")
123        W("                         - List of paths containing user-application assemblies and resources\n")
124 #ifndef NO_NGENPDB
125        W("    /App_Ni_Paths <path[") PATH_SEPARATOR_STR_W W("path]>\n")
126        W("                         - List of paths containing user-application native images\n")
127        W("                         - Must be used with /CreatePDB switch\n")
128 #endif // NO_NGENPDB
129
130        W("    /Platform_Assemblies_Paths <path[") PATH_SEPARATOR_STR_W W("path]>\n")
131        W("                         - List of paths containing target platform assemblies\n")
132        // If Platform_Assemblies_Paths, we will use it to build the TPA list and thus,
133        // TPA list cannot be explicitly specified.
134        W("                         - Cannot be used with Trusted_Platform_Assemblies\n")
135        
136 #ifdef FEATURE_COMINTEROP
137        W("    /Platform_Winmd_Paths <path[") PATH_SEPARATOR_STR_W W("path]>\n")
138        W("                         - List of paths containing target platform WinMDs used\n")
139        W("                           for emulating RoResolveNamespace\n")
140 #endif
141        W("    /MissingDependenciesOK\n")
142        W("                         - Specifies that crossgen should attempt not to fail\n")
143        W("                           if a dependency is missing.\n")
144 #if 0
145        W("    /Tuning              - Generate an instrumented image to collect\n")
146        W("                           scenario traces, which can be used with ibcmerge.exe\n")
147 #endif
148 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
149        W("    /JITPath <path>\n")
150        W("                         - Specifies the absolute file path to JIT compiler to be used.\n")
151 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
152 #ifdef FEATURE_READYTORUN_COMPILER
153        W("    /ReadyToRun          - Generate images resilient to the runtime and\n")
154        W("                           dependency versions\n")
155 #endif
156 #ifdef FEATURE_WINMD_RESILIENT
157        W(" WinMD Parameters\n")
158        W("    /WinMDResilient - Generate images resilient to WinMD dependency changes.\n")
159 #endif
160        W(" Size on Disk Parameters\n")
161        W("    /NoMetaData     - Do not copy metadata and IL into native image.\n")
162 #ifndef NO_NGENPDB
163        W(" Debugging Parameters\n")
164        W("    /CreatePDB <Dir to store PDB> [/lines [<search path for managed PDB>] ]\n")
165        W("        When specifying /CreatePDB, the native image should be created\n")
166        W("        first, and <assembly name> should be the path to the NI.\n")
167        W("    /DiasymreaderPath <Path to diasymreader.dll>\n")
168        W("        - Specifies the absolute file path to diasymreader.dll to be used.\n")
169 #elif defined(FEATURE_PERFMAP)
170        W(" Debugging Parameters\n")
171        W("    /CreatePerfMap <Dir to store perf map>\n")
172        W("        When specifying /CreatePerfMap, the native image should be created\n")
173        W("        first, and <assembly name> should be the path to the NI.\n")
174 #endif
175        );
176 }
177
178 class CrossgenLogger : public ICorSvcLogger
179 {
180     STDMETHODIMP_(ULONG)    AddRef()  {return E_NOTIMPL;}
181     STDMETHODIMP_(ULONG)    Release() {return E_NOTIMPL;}
182     STDMETHODIMP            QueryInterface(REFIID riid,void ** ppv)
183     {
184         if (ppv==0) 
185             return E_POINTER;
186         
187         *ppv = NULL;
188         
189         if (IsEqualIID(riid, IID_ICorSvcLogger) || IsEqualIID(riid, IID_IUnknown))
190         {
191             *ppv = this;
192             return S_OK;
193         }
194         else
195         {
196             return E_NOINTERFACE;
197         }
198     }
199
200     HRESULT STDMETHODCALLTYPE Log(        
201             /*[in] */CorSvcLogLevel logLevel,
202             /*[in] */BSTR message
203         )
204     {
205         if (logLevel == LogLevel_Error)
206             OutputErr(message);
207         else
208             Output(message);
209         return S_OK;
210     }
211 };
212
213 CrossgenLogger                g_CrossgenLogger;
214
215 //
216 // Tests whether szArg, the currently indexed argv matches the specified parameter name, szTestParamName.
217 // Specify szTestParamName without a switch.  This method handles testing for - and / switches.
218 //
219 bool MatchParameter(LPCWSTR szArg, LPCWSTR szTestParamName)
220 {
221     if (wcslen(szArg) == 0)
222     {
223         return false;
224     }
225
226     if (szArg[0] != W('/') && szArg[0] != W('-'))
227     {
228         return false;
229     }
230
231     return !_wcsicmp(szArg + 1, szTestParamName) || !_wcsicmp(szArg + 1, szTestParamName);
232 }
233
234 //
235 // Returns true if pwzString ends with the string in pwzCandidate
236 // Ignores case
237 //
238 bool StringEndsWith(LPCWSTR pwzString, LPCWSTR pwzCandidate)
239 {
240     size_t stringLength = wcslen(pwzString);
241     size_t candidateLength = wcslen(pwzCandidate);
242
243     if (candidateLength > stringLength || stringLength == 0 || candidateLength == 0)
244     {
245         return false;
246     }
247
248     LPCWSTR pwzStringEnd = pwzString + stringLength - candidateLength;
249
250     return !_wcsicmp(pwzStringEnd, pwzCandidate);
251 }
252
253 //
254 // When using the Phone binding model (TrustedPlatformAssemblies), automatically
255 // detect which path CoreLib.[ni.]dll lies in.
256 //
257 bool ComputeMscorlibPathFromTrustedPlatformAssemblies(SString& pwzMscorlibPath, LPCWSTR pwzTrustedPlatformAssemblies)
258 {
259     LPWSTR wszTrustedPathCopy = new WCHAR[wcslen(pwzTrustedPlatformAssemblies) + 1];
260     wcscpy_s(wszTrustedPathCopy, wcslen(pwzTrustedPlatformAssemblies) + 1, pwzTrustedPlatformAssemblies);
261     wchar_t *context;
262     LPWSTR wszSingleTrustedPath = wcstok_s(wszTrustedPathCopy, PATH_SEPARATOR_STR_W, &context);
263     
264     while (wszSingleTrustedPath != NULL)
265     {
266         size_t pathLength = wcslen(wszSingleTrustedPath);
267         // Strip off enclosing quotes, if present
268         if (wszSingleTrustedPath[0] == W('\"') && wszSingleTrustedPath[pathLength-1] == W('\"'))
269         {
270             wszSingleTrustedPath[pathLength-1] = '\0';
271             wszSingleTrustedPath++;
272         }
273
274         if (StringEndsWith(wszSingleTrustedPath, DIRECTORY_SEPARATOR_STR_W CoreLibName_IL_W) ||
275             StringEndsWith(wszSingleTrustedPath, DIRECTORY_SEPARATOR_STR_W CoreLibName_NI_W))
276         {
277             pwzMscorlibPath.Set(wszSingleTrustedPath);
278             SString::Iterator pwzSeparator = pwzMscorlibPath.End();
279             bool retval = true;
280             
281             if (!SUCCEEDED(CopySystemDirectory(pwzMscorlibPath, pwzMscorlibPath)))
282             {
283                 retval = false;
284             }
285
286             delete [] wszTrustedPathCopy;
287             return retval;
288         }
289         
290         wszSingleTrustedPath = wcstok_s(NULL, PATH_SEPARATOR_STR_W, &context);
291     }
292     delete [] wszTrustedPathCopy;
293
294     return false;
295 }
296
297 // Given a path terminated with "\\" and a search mask, this function will add
298 // the enumerated files, corresponding to the search mask, from the path into
299 // the refTPAList.
300 void PopulateTPAList(SString path, LPCWSTR pwszMask, SString &refTPAList, bool fCompilingMscorlib, bool fCreatePDB)
301 {
302     _ASSERTE(path.GetCount() > 0);
303     ClrDirectoryEnumerator folderEnumerator(path.GetUnicode(), pwszMask);
304     
305     while (folderEnumerator.Next())
306     {
307         // Got a valid enumeration handle and the data about the first file.
308         DWORD dwAttributes = folderEnumerator.GetFileAttributes();
309         if ((!(dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) && (!(dwAttributes & FILE_ATTRIBUTE_DEVICE)))
310         {
311             bool fAddDelimiter = (refTPAList.GetCount() > 0)?true:false;
312             bool fAddFileToTPAList = true;
313             LPCWSTR pwszFilename = folderEnumerator.GetFileName();
314             if (fCompilingMscorlib)
315             {
316                 // When compiling CoreLib, no ".ni.dll" should be on the TPAList.
317                 if (StringEndsWith((LPWSTR)pwszFilename, W(".ni.dll")))
318                 {
319                     fAddFileToTPAList = false;
320                 }
321             }
322             else
323             {
324                 // When creating PDBs, we must ensure that .ni.dlls are in the TPAList
325                 if (!fCreatePDB)
326                 {
327                     // Only CoreLib's ni.dll should be in the TPAList for the compilation of non-mscorlib assemblies.
328                     if (StringEndsWith((LPWSTR)pwszFilename, W(".ni.dll")))
329                     {
330                         if (!StringEndsWith((LPWSTR)pwszFilename, CoreLibName_NI_W))
331                         {
332                             fAddFileToTPAList = false;
333                         }
334                     }
335                 }
336                 
337                 // Ensure that CoreLib's IL version is also not on the TPAlist for this case.                
338                 if (StringEndsWith((LPWSTR)pwszFilename, CoreLibName_IL_W))
339                 {
340                     fAddFileToTPAList = false;
341                 }
342             }
343             
344             if (fAddFileToTPAList)
345             {
346                 if (fAddDelimiter)
347                 {
348                     // Add the path delimiter if we already have entries in the TPAList
349                     refTPAList.Append(PATH_SEPARATOR_CHAR_W);
350                 }
351                 // Add the path to the TPAList
352                 refTPAList.Append(path);
353                 refTPAList.Append(pwszFilename);
354             } 
355         }
356     }
357  }
358
359 // Given a semi-colon delimited set of absolute folder paths (pwzPlatformAssembliesPaths), this function
360 // will enumerate all EXE/DLL modules in those folders and add them to the TPAList buffer (refTPAList).
361 void ComputeTPAListFromPlatformAssembliesPath(LPCWSTR pwzPlatformAssembliesPaths, SString &refTPAList, bool fCompilingMscorlib, bool fCreatePDB)
362 {
363     // We should have a valid pointer to the paths
364     _ASSERTE(pwzPlatformAssembliesPaths != NULL);
365     
366     SString ssPlatformAssembliesPath(pwzPlatformAssembliesPaths);
367     
368     // Platform Assemblies Path List is semi-colon delimited
369     if(ssPlatformAssembliesPath.GetCount() > 0)
370     {
371         SString::CIterator start = ssPlatformAssembliesPath.Begin();
372         SString::CIterator itr = ssPlatformAssembliesPath.Begin();
373         SString::CIterator end = ssPlatformAssembliesPath.End();
374         SString qualifiedPath;
375
376         while (itr != end)
377         {
378             start = itr;
379             BOOL found = ssPlatformAssembliesPath.Find(itr, PATH_SEPARATOR_CHAR_W);
380             if (!found)
381             {
382                 itr = end;
383             }
384
385             SString qualifiedPath(ssPlatformAssembliesPath,start,itr);
386
387             if (found)
388             {
389                 itr++;
390             }
391
392             unsigned len = qualifiedPath.GetCount();
393
394             if (len > 0)
395             {
396                 if (qualifiedPath[len-1]!=DIRECTORY_SEPARATOR_CHAR_W)
397                 {
398                     qualifiedPath.Append(DIRECTORY_SEPARATOR_CHAR_W);
399                 }
400
401                 // Enumerate the EXE/DLL modules within this path and add them to the TPAList
402                 EX_TRY
403                 {
404                     PopulateTPAList(qualifiedPath, W("*.exe"), refTPAList, fCompilingMscorlib, fCreatePDB);
405                     PopulateTPAList(qualifiedPath, W("*.dll"), refTPAList, fCompilingMscorlib, fCreatePDB);
406                 }
407                 EX_CATCH
408                 {
409                     Outputf(W("Warning: Error enumerating files under %s.\n"), qualifiedPath.GetUnicode());
410                 }
411                 EX_END_CATCH(SwallowAllExceptions);
412             }
413         }
414     }
415 }
416
417 extern HMODULE g_hThisInst;
418
419 int _cdecl wmain(int argc, __in_ecount(argc) WCHAR **argv)
420 {
421 #ifndef FEATURE_PAL
422     g_hThisInst = WszGetModuleHandle(NULL);
423 #endif
424
425     /////////////////////////////////////////////////////////////////////////
426     //
427     // Parse the arguments
428     //
429     bool fDisplayLogo = true;
430     DWORD dwFlags = 0;
431     LPCWSTR pwzFilename = NULL;
432     LPCWSTR pwzPlatformResourceRoots = nullptr;
433     LPCWSTR pwzTrustedPlatformAssemblies = nullptr;
434     LPCWSTR pwzAppPaths = nullptr;
435     LPCWSTR pwzAppNiPaths = nullptr;
436     LPCWSTR pwzPlatformAssembliesPaths = nullptr;
437     LPCWSTR pwzPlatformWinmdPaths = nullptr;
438     StackSString wzDirectoryToStorePDB;
439     bool fCreatePDB = false;
440     bool fGeneratePDBLinesInfo = false;
441     LPWSTR pwzSearchPathForManagedPDB = NULL;
442     LPCWSTR pwzOutputFilename = NULL;
443     LPCWSTR pwzPublicKeys = nullptr;
444
445 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
446     LPCWSTR pwszCLRJITPath = nullptr;
447 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
448
449     LPCWSTR pwzDiasymreaderPath = nullptr;
450
451     HRESULT hr;
452
453 #ifndef PLATFORM_UNIX
454     // This is required to properly display Unicode characters
455     _setmode(_fileno(stdout), _O_U8TEXT);
456 #endif
457
458     // Skip this executable path
459     argv++;
460     argc--;
461
462     ConsoleArgs consoleArgs;
463     int argc2;
464     LPWSTR *argv2;
465
466     if (argc == 0)
467     {
468         PrintUsageHelper();
469         exit(INVALID_ARGUMENTS);
470     }
471     
472     if (!consoleArgs.ExpandResponseFiles(argc, argv, &argc2, &argv2))
473     {
474         if (consoleArgs.ErrorMessage() != nullptr)
475         {
476             wprintf(consoleArgs.ErrorMessage());
477             exit(FAILURE_RESULT);
478         }
479     }
480     
481     argc = argc2;
482     argv = argv2;
483
484     // By default, Crossgen will assume code-generation for fulltrust domains unless /PartialTrust switch is specified
485     dwFlags |= NGENWORKER_FLAGS_FULLTRUSTDOMAIN;
486
487     // By default, Crossgen will generate readytorun images unless /FragileNonVersionable switch is specified
488     dwFlags |= NGENWORKER_FLAGS_READYTORUN;
489
490     while (argc > 0)
491     {
492         if (MatchParameter(*argv, W("?"))
493             || MatchParameter(*argv, W("help")))
494         {
495             PrintUsageHelper();
496             exit(INVALID_ARGUMENTS);
497         }
498         else if (MatchParameter(*argv, W("nologo")))
499         {
500             fDisplayLogo = false;
501         }
502         else if (MatchParameter(*argv, W("silent")))
503         {
504             dwFlags |= NGENWORKER_FLAGS_SILENT;
505         }
506         else if (MatchParameter(*argv, W("Tuning")))
507         {
508             dwFlags |= NGENWORKER_FLAGS_TUNING;
509         }
510         else if (MatchParameter(*argv, W("MissingDependenciesOK")))
511         {
512             dwFlags |= NGENWORKER_FLAGS_MISSINGDEPENDENCIESOK;
513         }
514         else if (MatchParameter(*argv, W("PartialTrust")))
515         {
516             // Clear the /fulltrust flag
517             dwFlags = dwFlags & ~NGENWORKER_FLAGS_FULLTRUSTDOMAIN;
518         }
519         else if (MatchParameter(*argv, W("FullTrust")))
520         {
521             // Keep the "/fulltrust" switch around but let it be no-nop. Without this, any usage of /fulltrust will result in crossgen command-line
522             // parsing failure. Considering that scripts all over (CLR, Phone Build, etc) specify that switch, we let it be as opposed to going
523             // and fixing all the scripts.
524             //
525             // We dont explicitly set the flag here again so that if "/PartialTrust" is specified, then it will successfully override the default
526             // fulltrust behaviour.
527         }
528 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
529         else if (MatchParameter(*argv, W("JITPath")) && (argc > 1))
530         {
531             pwszCLRJITPath = argv[1];
532             
533             // skip JIT Path
534             argv++;
535             argc--;
536         }
537 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
538 #ifdef FEATURE_WINMD_RESILIENT
539         else if (MatchParameter(*argv, W("WinMDResilient")))
540         {
541             dwFlags |= NGENWORKER_FLAGS_WINMD_RESILIENT;
542         }
543 #endif
544 #ifdef FEATURE_READYTORUN_COMPILER
545         else if (MatchParameter(*argv, W("ReadyToRun")))
546         {
547             dwFlags |= NGENWORKER_FLAGS_READYTORUN;
548         }
549         else if (MatchParameter(*argv, W("FragileNonVersionable")))
550         {
551             dwFlags &= ~NGENWORKER_FLAGS_READYTORUN;
552         }
553 #endif
554         else if (MatchParameter(*argv, W("NoMetaData")))
555         {
556             dwFlags |= NGENWORKER_FLAGS_NO_METADATA;
557         }
558         else if (MatchParameter(*argv, W("out")))
559         {
560             if (pwzOutputFilename != NULL)
561             {
562                 Output(W("Cannot specify multiple output files.\n"));
563                 exit(INVALID_ARGUMENTS);
564             }
565             pwzOutputFilename = argv[1];
566             argv++;
567             argc--;
568         }
569         else if (MatchParameter(*argv, W("in")))
570         {
571             if (pwzFilename != NULL)
572             {
573                 Output(W("Cannot specify multiple input files.\n"));
574                 exit(INVALID_ARGUMENTS);
575             }
576             pwzFilename = argv[1];
577             argv++;
578             argc--;
579         }
580         else if (MatchParameter(*argv, W("Trusted_Platform_Assemblies")) && (argc > 1))
581         {
582             pwzTrustedPlatformAssemblies = argv[1];
583
584             // skip path list
585             argv++;
586             argc--;
587         }
588         else if (MatchParameter(*argv, W("Platform_Resource_Roots")) && (argc > 1))
589         {
590             pwzPlatformResourceRoots = argv[1];
591
592             // skip path list
593             argv++;
594             argc--;
595         }
596         else if (MatchParameter(*argv, W("App_Paths")) && (argc > 1))
597         {
598             pwzAppPaths = argv[1];
599
600             // skip User app path
601             argv++;
602             argc--;
603         }
604 #ifndef NO_NGENPDB
605         else if (MatchParameter(*argv, W("App_Ni_Paths")) && (argc > 1))
606         {
607             pwzAppNiPaths = argv[1];
608
609             // skip User app path
610             argv++;
611             argc--;
612         }
613 #endif // NO_NGENPDB
614         else if (MatchParameter(*argv, W("Platform_Assemblies_Paths")) && (argc > 1))
615         {
616             pwzPlatformAssembliesPaths = argv[1];
617             
618             // skip path list
619             argv++;
620             argc--;
621         }
622 #ifdef FEATURE_COMINTEROP
623         else if (MatchParameter(*argv, W("Platform_Winmd_Paths")) && (argc > 1))
624         {
625             pwzPlatformWinmdPaths = argv[1];
626
627             // skip User app path
628             argv++;
629             argc--;
630         }
631 #endif // FEATURE_COMINTEROP
632 #ifndef NO_NGENPDB
633         else if (MatchParameter(*argv, W("CreatePDB")) && (argc > 1))
634         {
635             // syntax: /CreatePDB <directory to store PDB> [/lines  [<search path for managed PDB>] ]
636             
637             // Parse: /CreatePDB
638             fCreatePDB = true;
639             argv++;
640             argc--;
641
642             // Clear the /fulltrust flag - /CreatePDB does not work with any other flags.
643             dwFlags = dwFlags & ~(NGENWORKER_FLAGS_FULLTRUSTDOMAIN | NGENWORKER_FLAGS_READYTORUN);
644
645             // Parse: <directory to store PDB>
646             wzDirectoryToStorePDB.Set(argv[0]);
647             argv++;
648             argc--;
649
650             // Ensure output dir ends in a backslash, or else diasymreader has issues
651             if (wzDirectoryToStorePDB[wzDirectoryToStorePDB.GetCount()-1] != DIRECTORY_SEPARATOR_CHAR_W)
652             {
653                 wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W);
654             }
655
656             if (argc == 0)
657             {
658                 Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n"));
659                 exit(FAILURE_RESULT);
660             }
661
662             // [/lines  [<search path for managed PDB>] ]
663             if (MatchParameter(*argv, W("lines")) && (argc > 1))
664             {
665                 // Parse: /lines
666                 fGeneratePDBLinesInfo = true;
667                 argv++;
668                 argc--;
669
670                 if (argc == 0)
671                 {
672                     Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n"));
673                     exit(FAILURE_RESULT);
674                 }
675
676                 if (argc > 1)
677                 {
678                     // Parse: <search path for managed PDB>
679                     pwzSearchPathForManagedPDB = argv[0];
680                     argv++;
681                     argc--;
682                 }
683             }
684
685             // Undo last arg iteration, since we do it for all cases at the bottom of
686             // the loop
687             argv--;
688             argc++;
689         }
690         else if (MatchParameter(*argv, W("DiasymreaderPath")) && (argc > 1))
691         {
692             pwzDiasymreaderPath = argv[1];
693
694             // skip diasymreader Path
695             argv++;
696             argc--;
697         }
698 #endif // NO_NGENPDB
699 #ifdef FEATURE_PERFMAP
700         else if (MatchParameter(*argv, W("CreatePerfMap")) && (argc > 1))
701         {
702             // syntax: /CreatePerfMap <directory to store perfmap>
703
704             // Parse: /CreatePerfMap
705             // NOTE: We use the same underlying PDB logic.
706             fCreatePDB = true;
707             argv++;
708             argc--;
709
710             // Clear the /fulltrust flag - /CreatePerfMap does not work with any other flags.
711             dwFlags = dwFlags & ~NGENWORKER_FLAGS_FULLTRUSTDOMAIN;
712
713             // Clear the /ready to run flag - /CreatePerfmap does not work with any other flags.
714             dwFlags = dwFlags & ~NGENWORKER_FLAGS_READYTORUN;
715
716             // Parse: <directory to store PDB>
717             wzDirectoryToStorePDB.Set(argv[0]);
718             argv++;
719             argc--;
720
721             // Ensure output dir ends in a backslash
722             if (wzDirectoryToStorePDB[wcslen(wzDirectoryToStorePDB)-1] != DIRECTORY_SEPARATOR_CHAR_W)
723             {
724                 wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W);
725             }
726
727             if (argc == 0)
728             {
729                 Output(W("The /CreatePerfMap switch requires <directory to store perfmap> and <assembly name>.\n"));
730                 exit(FAILURE_RESULT);
731             }
732
733             // Undo last arg iteration, since we do it for all cases at the bottom of
734             // the loop
735             argv--;
736             argc++;
737         }
738 #endif // FEATURE_PERFMAP
739         else
740         {
741             if (argc == 1)
742             {
743 #if !defined(FEATURE_PAL)
744                 // When not running on Mac, which can have forward-slash pathnames, we know
745                 // a command switch here means an invalid argument.
746                 if (*argv[0] == W('-') || *argv[0] == W('/'))
747                 {
748                     Outputf(W("Invalid parameter: %s\n"), *argv);
749                     exit(INVALID_ARGUMENTS);
750                 }
751 #endif //!FEATURE_PAL
752                 // The last thing on the command line is an assembly name or path, and
753                 // because we got this far is not an argument like /nologo. Because this
754                 // code works on Mac, with forward-slash pathnames, we can't assume
755                 // anything with a forward slash is an argument. So we just always
756                 // assume the last thing on the command line must be an assembly name.
757
758                 if (pwzFilename != NULL)
759                 {
760                     Output(W("Cannot use /In and specify an input file as the last argument.\n"));
761                     exit(INVALID_ARGUMENTS);
762                 }
763                 
764                 pwzFilename = *argv;
765                 break;
766             }
767             else
768             {
769                 Outputf(W("Invalid parameter: %s\n"), *argv);
770                 exit(INVALID_ARGUMENTS);
771             }
772         }
773
774         argv++;
775         argc--;
776     }
777
778     if (pwzFilename == NULL)
779     {
780         Output(W("You must specify an assembly to compile\n"));
781         exit(INVALID_ARGUMENTS);
782     }
783
784     if (fCreatePDB && (dwFlags != 0))
785     {
786         Output(W("The /CreatePDB switch cannot be used with other switches, except /lines and the various path switches.\n"));
787         exit(FAILURE_RESULT);
788     }
789
790     if (pwzAppNiPaths != nullptr && !fCreatePDB)
791     {
792         Output(W("The /App_Ni_Paths switch can only be used with the /CreatePDB switch.\n"));
793         exit(FAILURE_RESULT);
794     }
795
796 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
797     if (pwszCLRJITPath != nullptr && fCreatePDB)
798     {
799         Output(W("The /JITPath switch can not be used with the /CreatePDB switch.\n"));
800         exit(FAILURE_RESULT);
801     }
802 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
803
804 #if !defined(NO_NGENPDB)
805     if (pwzDiasymreaderPath != nullptr && !fCreatePDB)
806     {
807         Output(W("The /DiasymreaderPath switch can only be used with the /CreatePDB switch.\n"));
808         exit(FAILURE_RESULT);
809     }
810 #endif // !defined(NO_NGENPDB)
811
812     if ((pwzTrustedPlatformAssemblies != nullptr) && (pwzPlatformAssembliesPaths != nullptr))
813     {
814         Output(W("The /Trusted_Platform_Assemblies and /Platform_Assemblies_Paths switches cannot be both specified.\n"));
815         exit(FAILURE_RESULT);
816     }
817
818     if ((dwFlags & NGENWORKER_FLAGS_NO_METADATA) != 0)
819     {
820         const size_t windowsDotWinmdLength = 13;    // Length of string "Windows.winmd"
821         size_t filenameLength = wcslen(pwzFilename);
822         bool isWindowsDotWinmd = true;
823         if (filenameLength < windowsDotWinmdLength ||
824             _wcsicmp(pwzFilename + filenameLength - windowsDotWinmdLength, W("windows.winmd")) != 0)
825         {
826             isWindowsDotWinmd = false;
827         }
828         else if (filenameLength > windowsDotWinmdLength)
829         {
830             WCHAR pathSeparator = pwzFilename[filenameLength - windowsDotWinmdLength - 1];
831             if (pathSeparator != W('\\') && pathSeparator != W('/') && pathSeparator != W(':'))
832             {
833                 isWindowsDotWinmd = false;
834             }
835         }
836         if (!isWindowsDotWinmd)
837         {
838             Output(W("The /NoMetaData switch can only be used with Windows.winmd.\n"));
839             exit(FAILURE_RESULT);
840         }
841     }
842     
843     // All argument processing has happened by now. The only messages that should appear before here are errors
844     // related to argument parsing, such as the Usage message. Afterwards, other messages can appear.
845
846     /////////////////////////////////////////////////////////////////////////
847     //
848     // Start processing
849     //
850
851     if (fDisplayLogo)
852     {
853         PrintLogoHelper();
854     }
855
856     PathString wzTrustedPathRoot;
857
858     SString ssTPAList;  
859
860     if (fCreatePDB)
861     {
862         // While creating PDB, assembly binder gives preference to files in TPA.
863         // This can create difficulties if the input file is not in TPA.
864         // To avoid this issue, put the input file as the first item in TPA.
865         ssTPAList.Append(pwzFilename);
866     }
867     
868     // Are we compiling mscorlib.dll? 
869     bool fCompilingMscorlib = StringEndsWith((LPWSTR)pwzFilename, CoreLibName_IL_W);
870
871     if (fCompilingMscorlib)
872         dwFlags &= ~NGENWORKER_FLAGS_READYTORUN;
873
874     if(pwzPlatformAssembliesPaths != nullptr)
875     {
876         // Platform_Assemblies_Paths command line switch has been specified.
877         _ASSERTE(pwzTrustedPlatformAssemblies == nullptr);
878         
879         // Formulate the TPAList from Platform_Assemblies_Paths
880         ComputeTPAListFromPlatformAssembliesPath(pwzPlatformAssembliesPaths, ssTPAList, fCompilingMscorlib, fCreatePDB);
881         pwzTrustedPlatformAssemblies = (WCHAR *)ssTPAList.GetUnicode();
882         pwzPlatformAssembliesPaths = NULL;
883     }
884
885     if (pwzTrustedPlatformAssemblies != nullptr)
886     {
887         if (ComputeMscorlibPathFromTrustedPlatformAssemblies(wzTrustedPathRoot, pwzTrustedPlatformAssemblies))
888         {
889             pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode();
890             SetMscorlibPath(pwzPlatformAssembliesPaths);
891         }
892     }
893
894     if (pwzPlatformAssembliesPaths == NULL)
895     {
896         if (!WszGetModuleFileName(NULL, wzTrustedPathRoot))
897         {
898             ERROR_WIN32(W("Error: GetModuleFileName failed (%d)\n"), GetLastError());
899             exit(CLR_INIT_ERROR);
900         }
901         
902         if (SUCCEEDED(CopySystemDirectory(wzTrustedPathRoot, wzTrustedPathRoot)))
903         {
904             pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode();
905         }
906         else
907         {
908             ERROR_HR(W("Error: wcsrchr returned NULL; GetModuleFileName must have given us something bad\n"), E_UNEXPECTED);
909             exit(CLR_INIT_ERROR);
910         }
911         
912         
913     }
914
915     // Initialize the logger
916     SetSvcLogger(&g_CrossgenLogger);
917
918     //Step - Compile the assembly
919
920     if (fCreatePDB)
921     {
922         hr = CreatePDBWorker(
923             pwzFilename, 
924             pwzPlatformAssembliesPaths, 
925             pwzTrustedPlatformAssemblies, 
926             pwzPlatformResourceRoots, 
927             pwzAppPaths, 
928             pwzAppNiPaths,
929             wzDirectoryToStorePDB, 
930             fGeneratePDBLinesInfo, 
931             pwzSearchPathForManagedPDB,
932             pwzPlatformWinmdPaths,
933             pwzDiasymreaderPath);
934         
935     }
936     else
937     {
938         hr = NGenWorker(pwzFilename, dwFlags,
939          pwzPlatformAssembliesPaths,
940          pwzTrustedPlatformAssemblies,
941          pwzPlatformResourceRoots,
942          pwzAppPaths,
943          pwzOutputFilename,
944          pwzPlatformWinmdPaths
945 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
946         ,
947         NULL, // ICorSvcLogger
948         pwszCLRJITPath   
949 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
950          );
951     }
952     
953
954     if (FAILED(hr))
955     {
956         if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
957         {
958             OutputErrf(W("Error: file \"%s\" or one of its dependencies was not found\n"), pwzFilename);
959             exit(ASSEMBLY_NOT_FOUND);
960         }
961         else
962         {
963             OutputErrf(W("Error: compilation failed for \"%s\" (0x%08x)\n"), pwzFilename, hr);
964             exit(hr);
965         }
966     }
967
968     return 0;
969 }
970
971 #ifdef PLATFORM_UNIX
972 int main(int argc, char *argv[])
973 {
974     if (0 != PAL_Initialize(argc, argv))
975     {
976         return FAILURE_RESULT;
977     }
978
979     wchar_t **wargv = new wchar_t*[argc];
980     for (int i = 0; i < argc; i++)
981     {
982         size_t len = strlen(argv[i]) + 1;
983         wargv[i] = new wchar_t[len];
984         WszMultiByteToWideChar(CP_ACP, 0, argv[i], -1, wargv[i], len);
985     }
986
987     int ret = wmain(argc, wargv);
988
989     for (int i = 0; i < argc; i++)
990     {
991         delete[] wargv[i];
992     }
993     delete[] wargv;
994
995     return ret;
996 }
997 #endif // PLATFORM_UNIX