Switch source build property to DotNetBuildFromSource
[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("    /verbose             - Display verbose information\n")
113        W("    @response.rsp        - Process command line arguments from specified\n")
114        W("                           response file\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             
315             // No NIs are supported when creating NI images (other than NI of System.Private.CoreLib.dll).
316             if (!fCreatePDB)
317             {
318                 // Only CoreLib's ni.dll should be in the TPAList for the compilation of non-mscorlib assemblies.
319                 if (StringEndsWith((LPWSTR)pwszFilename, W(".ni.dll")))
320                 {
321                     fAddFileToTPAList = false;
322                 }
323             }
324             
325             if (fAddFileToTPAList)
326             {
327                 if (fAddDelimiter)
328                 {
329                     // Add the path delimiter if we already have entries in the TPAList
330                     refTPAList.Append(PATH_SEPARATOR_CHAR_W);
331                 }
332                 // Add the path to the TPAList
333                 refTPAList.Append(path);
334                 refTPAList.Append(pwszFilename);
335             } 
336         }
337     }
338  }
339
340 // Given a semi-colon delimited set of absolute folder paths (pwzPlatformAssembliesPaths), this function
341 // will enumerate all EXE/DLL modules in those folders and add them to the TPAList buffer (refTPAList).
342 void ComputeTPAListFromPlatformAssembliesPath(LPCWSTR pwzPlatformAssembliesPaths, SString &refTPAList, bool fCompilingMscorlib, bool fCreatePDB)
343 {
344     // We should have a valid pointer to the paths
345     _ASSERTE(pwzPlatformAssembliesPaths != NULL);
346     
347     SString ssPlatformAssembliesPath(pwzPlatformAssembliesPaths);
348     
349     // Platform Assemblies Path List is semi-colon delimited
350     if(ssPlatformAssembliesPath.GetCount() > 0)
351     {
352         SString::CIterator start = ssPlatformAssembliesPath.Begin();
353         SString::CIterator itr = ssPlatformAssembliesPath.Begin();
354         SString::CIterator end = ssPlatformAssembliesPath.End();
355         SString qualifiedPath;
356
357         while (itr != end)
358         {
359             start = itr;
360             BOOL found = ssPlatformAssembliesPath.Find(itr, PATH_SEPARATOR_CHAR_W);
361             if (!found)
362             {
363                 itr = end;
364             }
365
366             SString qualifiedPath(ssPlatformAssembliesPath,start,itr);
367
368             if (found)
369             {
370                 itr++;
371             }
372
373             unsigned len = qualifiedPath.GetCount();
374
375             if (len > 0)
376             {
377                 if (qualifiedPath[len-1]!=DIRECTORY_SEPARATOR_CHAR_W)
378                 {
379                     qualifiedPath.Append(DIRECTORY_SEPARATOR_CHAR_W);
380                 }
381
382                 // Enumerate the EXE/DLL modules within this path and add them to the TPAList
383                 EX_TRY
384                 {
385                     PopulateTPAList(qualifiedPath, W("*.exe"), refTPAList, fCompilingMscorlib, fCreatePDB);
386                     PopulateTPAList(qualifiedPath, W("*.dll"), refTPAList, fCompilingMscorlib, fCreatePDB);
387                 }
388                 EX_CATCH
389                 {
390                     Outputf(W("Warning: Error enumerating files under %s.\n"), qualifiedPath.GetUnicode());
391                 }
392                 EX_END_CATCH(SwallowAllExceptions);
393             }
394         }
395     }
396 }
397
398 extern HMODULE g_hThisInst;
399
400 int _cdecl wmain(int argc, __in_ecount(argc) WCHAR **argv)
401 {
402 #ifndef FEATURE_PAL
403     g_hThisInst = WszGetModuleHandle(NULL);
404 #endif
405
406     /////////////////////////////////////////////////////////////////////////
407     //
408     // Parse the arguments
409     //
410     bool fDisplayLogo = true;
411     DWORD dwFlags = 0;
412     LPCWSTR pwzFilename = NULL;
413     LPCWSTR pwzPlatformResourceRoots = nullptr;
414     LPCWSTR pwzTrustedPlatformAssemblies = nullptr;
415     LPCWSTR pwzAppPaths = nullptr;
416     LPCWSTR pwzAppNiPaths = nullptr;
417     LPCWSTR pwzPlatformAssembliesPaths = nullptr;
418     LPCWSTR pwzPlatformWinmdPaths = nullptr;
419     StackSString wzDirectoryToStorePDB;
420     bool fCreatePDB = false;
421     bool fGeneratePDBLinesInfo = false;
422     LPWSTR pwzSearchPathForManagedPDB = NULL;
423     LPCWSTR pwzOutputFilename = NULL;
424     LPCWSTR pwzPublicKeys = nullptr;
425
426 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
427     LPCWSTR pwszCLRJITPath = nullptr;
428 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
429
430     LPCWSTR pwzDiasymreaderPath = nullptr;
431
432     HRESULT hr;
433
434 #ifndef PLATFORM_UNIX
435     // This is required to properly display Unicode characters
436     _setmode(_fileno(stdout), _O_U8TEXT);
437 #endif
438
439     // Skip this executable path
440     argv++;
441     argc--;
442
443     ConsoleArgs consoleArgs;
444     int argc2;
445     LPWSTR *argv2;
446
447     if (argc == 0)
448     {
449         PrintUsageHelper();
450         exit(INVALID_ARGUMENTS);
451     }
452     
453     if (!consoleArgs.ExpandResponseFiles(argc, argv, &argc2, &argv2))
454     {
455         if (consoleArgs.ErrorMessage() != nullptr)
456         {
457             wprintf(consoleArgs.ErrorMessage());
458             exit(FAILURE_RESULT);
459         }
460     }
461     
462     argc = argc2;
463     argv = argv2;
464
465     // By default, Crossgen will generate readytorun images unless /FragileNonVersionable switch is specified
466     dwFlags |= NGENWORKER_FLAGS_READYTORUN;
467
468     while (argc > 0)
469     {
470         if (MatchParameter(*argv, W("?"))
471             || MatchParameter(*argv, W("help")))
472         {
473             PrintUsageHelper();
474             exit(INVALID_ARGUMENTS);
475         }
476         else if (MatchParameter(*argv, W("nologo")))
477         {
478             fDisplayLogo = false;
479         }
480         else if (MatchParameter(*argv, W("silent")))
481         {
482             dwFlags |= NGENWORKER_FLAGS_SILENT;
483         }
484         else if (MatchParameter(*argv, W("verbose")))
485         {
486             dwFlags |= NGENWORKER_FLAGS_VERBOSE;
487         }
488         else if (MatchParameter(*argv, W("Tuning")))
489         {
490             dwFlags |= NGENWORKER_FLAGS_TUNING;
491         }
492         else if (MatchParameter(*argv, W("MissingDependenciesOK")))
493         {
494             dwFlags |= NGENWORKER_FLAGS_MISSINGDEPENDENCIESOK;
495         }
496 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
497         else if (MatchParameter(*argv, W("JITPath")) && (argc > 1))
498         {
499             pwszCLRJITPath = argv[1];
500             
501             // skip JIT Path
502             argv++;
503             argc--;
504         }
505 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
506 #ifdef FEATURE_WINMD_RESILIENT
507         else if (MatchParameter(*argv, W("WinMDResilient")))
508         {
509             dwFlags |= NGENWORKER_FLAGS_WINMD_RESILIENT;
510         }
511 #endif
512 #ifdef FEATURE_READYTORUN_COMPILER
513         else if (MatchParameter(*argv, W("ReadyToRun")))
514         {
515             dwFlags |= NGENWORKER_FLAGS_READYTORUN;
516         }
517         else if (MatchParameter(*argv, W("FragileNonVersionable")))
518         {
519             dwFlags &= ~NGENWORKER_FLAGS_READYTORUN;
520         }
521 #endif
522         else if (MatchParameter(*argv, W("NoMetaData")))
523         {
524             dwFlags |= NGENWORKER_FLAGS_NO_METADATA;
525         }
526         else if (MatchParameter(*argv, W("out")))
527         {
528             if (pwzOutputFilename != NULL)
529             {
530                 Output(W("Cannot specify multiple output files.\n"));
531                 exit(INVALID_ARGUMENTS);
532             }
533             pwzOutputFilename = argv[1];
534             argv++;
535             argc--;
536         }
537         else if (MatchParameter(*argv, W("in")))
538         {
539             if (pwzFilename != NULL)
540             {
541                 Output(W("Cannot specify multiple input files.\n"));
542                 exit(INVALID_ARGUMENTS);
543             }
544             pwzFilename = argv[1];
545             argv++;
546             argc--;
547         }
548         else if (MatchParameter(*argv, W("Trusted_Platform_Assemblies")) && (argc > 1))
549         {
550             pwzTrustedPlatformAssemblies = argv[1];
551
552             // skip path list
553             argv++;
554             argc--;
555         }
556         else if (MatchParameter(*argv, W("Platform_Resource_Roots")) && (argc > 1))
557         {
558             pwzPlatformResourceRoots = argv[1];
559
560             // skip path list
561             argv++;
562             argc--;
563         }
564         else if (MatchParameter(*argv, W("App_Paths")) && (argc > 1))
565         {
566             pwzAppPaths = argv[1];
567
568             // skip User app path
569             argv++;
570             argc--;
571         }
572 #ifndef NO_NGENPDB
573         else if (MatchParameter(*argv, W("App_Ni_Paths")) && (argc > 1))
574         {
575             pwzAppNiPaths = argv[1];
576
577             // skip User app path
578             argv++;
579             argc--;
580         }
581 #endif // NO_NGENPDB
582         else if (MatchParameter(*argv, W("Platform_Assemblies_Paths")) && (argc > 1))
583         {
584             pwzPlatformAssembliesPaths = argv[1];
585             
586             // skip path list
587             argv++;
588             argc--;
589         }
590 #ifdef FEATURE_COMINTEROP
591         else if (MatchParameter(*argv, W("Platform_Winmd_Paths")) && (argc > 1))
592         {
593             pwzPlatformWinmdPaths = argv[1];
594
595             // skip User app path
596             argv++;
597             argc--;
598         }
599 #endif // FEATURE_COMINTEROP
600 #ifndef NO_NGENPDB
601         else if (MatchParameter(*argv, W("CreatePDB")) && (argc > 1))
602         {
603             // syntax: /CreatePDB <directory to store PDB> [/lines  [<search path for managed PDB>] ]
604             
605             // Parse: /CreatePDB
606             fCreatePDB = true;
607             argv++;
608             argc--;
609
610             // Clear any extra flags - using /CreatePDB fails if any of these are set.
611             dwFlags = dwFlags & ~NGENWORKER_FLAGS_READYTORUN;
612
613             // Parse: <directory to store PDB>
614             wzDirectoryToStorePDB.Set(argv[0]);
615             argv++;
616             argc--;
617
618             // Ensure output dir ends in a backslash, or else diasymreader has issues
619             if (wzDirectoryToStorePDB[wzDirectoryToStorePDB.GetCount()-1] != DIRECTORY_SEPARATOR_CHAR_W)
620             {
621                 wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W);
622             }
623
624             if (argc == 0)
625             {
626                 Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n"));
627                 exit(FAILURE_RESULT);
628             }
629
630             // [/lines  [<search path for managed PDB>] ]
631             if (MatchParameter(*argv, W("lines")) && (argc > 1))
632             {
633                 // Parse: /lines
634                 fGeneratePDBLinesInfo = true;
635                 argv++;
636                 argc--;
637
638                 if (argc == 0)
639                 {
640                     Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n"));
641                     exit(FAILURE_RESULT);
642                 }
643
644                 if (argc > 1)
645                 {
646                     // Parse: <search path for managed PDB>
647                     pwzSearchPathForManagedPDB = argv[0];
648                     argv++;
649                     argc--;
650                 }
651             }
652
653             // Undo last arg iteration, since we do it for all cases at the bottom of
654             // the loop
655             argv--;
656             argc++;
657         }
658         else if (MatchParameter(*argv, W("DiasymreaderPath")) && (argc > 1))
659         {
660             pwzDiasymreaderPath = argv[1];
661
662             // skip diasymreader Path
663             argv++;
664             argc--;
665         }
666 #endif // NO_NGENPDB
667 #ifdef FEATURE_PERFMAP
668         else if (MatchParameter(*argv, W("CreatePerfMap")) && (argc > 1))
669         {
670             // syntax: /CreatePerfMap <directory to store perfmap>
671
672             // Parse: /CreatePerfMap
673             // NOTE: We use the same underlying PDB logic.
674             fCreatePDB = true;
675             argv++;
676             argc--;
677
678             // Clear the /ready to run flag - /CreatePerfmap does not work with any other flags.
679             dwFlags = dwFlags & ~NGENWORKER_FLAGS_READYTORUN;
680
681             // Parse: <directory to store PDB>
682             wzDirectoryToStorePDB.Set(argv[0]);
683             argv++;
684             argc--;
685
686             // Ensure output dir ends in a backslash
687             if (wzDirectoryToStorePDB[wcslen(wzDirectoryToStorePDB)-1] != DIRECTORY_SEPARATOR_CHAR_W)
688             {
689                 wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W);
690             }
691
692             if (argc == 0)
693             {
694                 Output(W("The /CreatePerfMap switch requires <directory to store perfmap> and <assembly name>.\n"));
695                 exit(FAILURE_RESULT);
696             }
697
698             // Undo last arg iteration, since we do it for all cases at the bottom of
699             // the loop
700             argv--;
701             argc++;
702         }
703 #endif // FEATURE_PERFMAP
704         else
705         {
706             if (argc == 1)
707             {
708 #if !defined(FEATURE_PAL)
709                 // When not running on Mac, which can have forward-slash pathnames, we know
710                 // a command switch here means an invalid argument.
711                 if (*argv[0] == W('-') || *argv[0] == W('/'))
712                 {
713                     Outputf(W("Invalid parameter: %s\n"), *argv);
714                     exit(INVALID_ARGUMENTS);
715                 }
716 #endif //!FEATURE_PAL
717                 // The last thing on the command line is an assembly name or path, and
718                 // because we got this far is not an argument like /nologo. Because this
719                 // code works on Mac, with forward-slash pathnames, we can't assume
720                 // anything with a forward slash is an argument. So we just always
721                 // assume the last thing on the command line must be an assembly name.
722
723                 if (pwzFilename != NULL)
724                 {
725                     Output(W("Cannot use /In and specify an input file as the last argument.\n"));
726                     exit(INVALID_ARGUMENTS);
727                 }
728                 
729                 pwzFilename = *argv;
730                 break;
731             }
732             else
733             {
734                 Outputf(W("Invalid parameter: %s\n"), *argv);
735                 exit(INVALID_ARGUMENTS);
736             }
737         }
738
739         argv++;
740         argc--;
741     }
742
743     if (pwzFilename == NULL)
744     {
745         Output(W("You must specify an assembly to compile\n"));
746         exit(INVALID_ARGUMENTS);
747     }
748
749     if (fCreatePDB && (dwFlags != 0))
750     {
751         Output(W("The /CreatePDB switch cannot be used with other switches, except /lines and the various path switches.\n"));
752         exit(FAILURE_RESULT);
753     }
754
755     if (pwzAppNiPaths != nullptr && !fCreatePDB)
756     {
757         Output(W("The /App_Ni_Paths switch can only be used with the /CreatePDB switch.\n"));
758         exit(FAILURE_RESULT);
759     }
760
761 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
762     if (pwszCLRJITPath != nullptr && fCreatePDB)
763     {
764         Output(W("The /JITPath switch can not be used with the /CreatePDB switch.\n"));
765         exit(FAILURE_RESULT);
766     }
767 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
768
769 #if !defined(NO_NGENPDB)
770     if (pwzDiasymreaderPath != nullptr && !fCreatePDB)
771     {
772         Output(W("The /DiasymreaderPath switch can only be used with the /CreatePDB switch.\n"));
773         exit(FAILURE_RESULT);
774     }
775 #endif // !defined(NO_NGENPDB)
776
777     if ((pwzTrustedPlatformAssemblies != nullptr) && (pwzPlatformAssembliesPaths != nullptr))
778     {
779         Output(W("The /Trusted_Platform_Assemblies and /Platform_Assemblies_Paths switches cannot be both specified.\n"));
780         exit(FAILURE_RESULT);
781     }
782
783     if ((dwFlags & NGENWORKER_FLAGS_NO_METADATA) != 0)
784     {
785         const size_t windowsDotWinmdLength = 13;    // Length of string "Windows.winmd"
786         size_t filenameLength = wcslen(pwzFilename);
787         bool isWindowsDotWinmd = true;
788         if (filenameLength < windowsDotWinmdLength ||
789             _wcsicmp(pwzFilename + filenameLength - windowsDotWinmdLength, W("windows.winmd")) != 0)
790         {
791             isWindowsDotWinmd = false;
792         }
793         else if (filenameLength > windowsDotWinmdLength)
794         {
795             WCHAR pathSeparator = pwzFilename[filenameLength - windowsDotWinmdLength - 1];
796             if (pathSeparator != W('\\') && pathSeparator != W('/') && pathSeparator != W(':'))
797             {
798                 isWindowsDotWinmd = false;
799             }
800         }
801         if (!isWindowsDotWinmd)
802         {
803             Output(W("The /NoMetaData switch can only be used with Windows.winmd.\n"));
804             exit(FAILURE_RESULT);
805         }
806     }
807     
808     // All argument processing has happened by now. The only messages that should appear before here are errors
809     // related to argument parsing, such as the Usage message. Afterwards, other messages can appear.
810
811     /////////////////////////////////////////////////////////////////////////
812     //
813     // Start processing
814     //
815
816     if (fDisplayLogo)
817     {
818         PrintLogoHelper();
819     }
820
821     PathString wzTrustedPathRoot;
822
823     SString ssTPAList;  
824
825     if (fCreatePDB)
826     {
827         // While creating PDB, assembly binder gives preference to files in TPA.
828         // This can create difficulties if the input file is not in TPA.
829         // To avoid this issue, put the input file as the first item in TPA.
830         ssTPAList.Append(pwzFilename);
831     }
832     
833     // Are we compiling mscorlib.dll? 
834     bool fCompilingMscorlib = StringEndsWith((LPWSTR)pwzFilename, CoreLibName_IL_W);
835
836 // Disable fragile NGen when compiling Mscorlib for ARM.
837 #if !(defined(_TARGET_ARM_) || defined(_TARGET_ARM64_))
838     if (fCompilingMscorlib)
839         dwFlags &= ~NGENWORKER_FLAGS_READYTORUN;
840 #endif // !(_TARGET_ARM_ || _TARGET_ARM64_)
841
842     if(pwzPlatformAssembliesPaths != nullptr)
843     {
844         // Platform_Assemblies_Paths command line switch has been specified.
845         _ASSERTE(pwzTrustedPlatformAssemblies == nullptr);
846         
847         // Formulate the TPAList from Platform_Assemblies_Paths
848         ComputeTPAListFromPlatformAssembliesPath(pwzPlatformAssembliesPaths, ssTPAList, fCompilingMscorlib, fCreatePDB);
849         pwzTrustedPlatformAssemblies = (WCHAR *)ssTPAList.GetUnicode();
850         pwzPlatformAssembliesPaths = NULL;
851     }
852
853     if (pwzTrustedPlatformAssemblies != nullptr)
854     {
855         if (ComputeMscorlibPathFromTrustedPlatformAssemblies(wzTrustedPathRoot, pwzTrustedPlatformAssemblies))
856         {
857             pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode();
858             SetMscorlibPath(pwzPlatformAssembliesPaths);
859         }
860     }
861
862     if (pwzPlatformAssembliesPaths == NULL)
863     {
864         if (!WszGetModuleFileName(NULL, wzTrustedPathRoot))
865         {
866             ERROR_WIN32(W("Error: GetModuleFileName failed (%d)\n"), GetLastError());
867             exit(CLR_INIT_ERROR);
868         }
869         
870         if (SUCCEEDED(CopySystemDirectory(wzTrustedPathRoot, wzTrustedPathRoot)))
871         {
872             pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode();
873         }
874         else
875         {
876             ERROR_HR(W("Error: wcsrchr returned NULL; GetModuleFileName must have given us something bad\n"), E_UNEXPECTED);
877             exit(CLR_INIT_ERROR);
878         }
879         
880         
881     }
882
883     // Initialize the logger
884     SetSvcLogger(&g_CrossgenLogger);
885
886     //Step - Compile the assembly
887
888     if (fCreatePDB)
889     {
890         hr = CreatePDBWorker(
891             pwzFilename, 
892             pwzPlatformAssembliesPaths, 
893             pwzTrustedPlatformAssemblies, 
894             pwzPlatformResourceRoots, 
895             pwzAppPaths, 
896             pwzAppNiPaths,
897             wzDirectoryToStorePDB, 
898             fGeneratePDBLinesInfo, 
899             pwzSearchPathForManagedPDB,
900             pwzPlatformWinmdPaths,
901             pwzDiasymreaderPath);
902         
903     }
904     else
905     {
906         hr = NGenWorker(pwzFilename, dwFlags,
907          pwzPlatformAssembliesPaths,
908          pwzTrustedPlatformAssemblies,
909          pwzPlatformResourceRoots,
910          pwzAppPaths,
911          pwzOutputFilename,
912          pwzPlatformWinmdPaths
913 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
914         ,
915         NULL, // ICorSvcLogger
916         pwszCLRJITPath   
917 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
918          );
919     }
920     
921
922     if (FAILED(hr))
923     {
924         if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
925         {
926             OutputErrf(W("Error: file \"%s\" or one of its dependencies was not found\n"), pwzFilename);
927             exit(ASSEMBLY_NOT_FOUND);
928         }
929         else
930         {
931             OutputErrf(W("Error: compilation failed for \"%s\" (0x%08x)\n"), pwzFilename, hr);
932             exit(hr);
933         }
934     }
935
936     return 0;
937 }
938
939 #ifdef PLATFORM_UNIX
940 int main(int argc, char *argv[])
941 {
942     if (0 != PAL_Initialize(argc, argv))
943     {
944         return FAILURE_RESULT;
945     }
946
947     wchar_t **wargv = new wchar_t*[argc];
948     for (int i = 0; i < argc; i++)
949     {
950         size_t len = strlen(argv[i]) + 1;
951         wargv[i] = new wchar_t[len];
952         WszMultiByteToWideChar(CP_ACP, 0, argv[i], -1, wargv[i], len);
953     }
954
955     int ret = wmain(argc, wargv);
956
957     for (int i = 0; i < argc; i++)
958     {
959         delete[] wargv[i];
960     }
961     delete[] wargv;
962
963     return ret;
964 }
965 #endif // PLATFORM_UNIX