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
26enum 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
36STDAPI CreatePDBWorker(LPCWSTR pwzAssemblyPath, LPCWSTR pwzPlatformAssembliesPaths, LPCWSTR pwzTrustedPlatformAssemblies, LPCWSTR pwzPlatformResourceRoots, LPCWSTR pwzAppPaths, LPCWSTR pwzAppNiPaths, LPCWSTR pwzPdbPath, BOOL fGeneratePDBLinesInfo, LPCWSTR pwzManagedPdbSearchPath, LPCWSTR pwzPlatformWinmdPaths, LPCWSTR pwzDiasymreaderPath);
37STDAPI 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);
38void SetSvcLogger(ICorSvcLogger *pCorSvcLogger);
39void SetMscorlibPath(LPCWSTR wzSystemDirectory);
40
41/* --------------------------------------------------------------------------- *
42 * Console stuff
43 * --------------------------------------------------------------------------- */
44
45void Output(LPCWSTR str)
46{
47 wprintf(W("%s"), str);
48}
49
50void Outputf(LPCWSTR szFormat, ...)
51{
52 va_list args;
53 va_start(args, szFormat);
54 vfwprintf(stdout, szFormat, args);
55 va_end(args);
56}
57
58void OutputErr(LPCWSTR str)
59{
60 fwprintf(stderr, W("%s"), str);
61}
62
63void OutputErrf(LPCWSTR szFormat, ...)
64{
65 va_list args;
66 va_start(args, szFormat);
67 vfwprintf(stderr, szFormat, args);
68 va_end(args);
69}
70
71void ErrorHR(HRESULT hr)
72{
73 OutputErrf(W("Error: failed to initialize CoreCLR: 0x%08x\n"), hr);
74}
75
76void 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
93void 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
101void 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
178class 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
213CrossgenLogger 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//
219bool 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//
238bool 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//
257bool 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.
300void 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).
342void 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
398extern HMODULE g_hThisInst;
399
400int _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 = 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 bool fExplicitReadyToRunSwitch = false;
426
427#if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
428 LPCWSTR pwszCLRJITPath = nullptr;
429#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
430
431 LPCWSTR pwzDiasymreaderPath = nullptr;
432
433 HRESULT hr;
434
435#ifndef PLATFORM_UNIX
436 // This is required to properly display Unicode characters
437 _setmode(_fileno(stdout), _O_U8TEXT);
438#endif
439
440 // Skip this executable path
441 argv++;
442 argc--;
443
444 ConsoleArgs consoleArgs;
445 int argc2;
446 LPWSTR *argv2;
447
448 if (argc == 0)
449 {
450 PrintUsageHelper();
451 exit(INVALID_ARGUMENTS);
452 }
453
454 if (!consoleArgs.ExpandResponseFiles(argc, argv, &argc2, &argv2))
455 {
456 if (consoleArgs.ErrorMessage() != nullptr)
457 {
458 wprintf(consoleArgs.ErrorMessage());
459 exit(FAILURE_RESULT);
460 }
461 }
462
463 argc = argc2;
464 argv = argv2;
465
466 // By default, Crossgen will generate readytorun images unless /FragileNonVersionable switch is specified
467 dwFlags |= NGENWORKER_FLAGS_READYTORUN;
468
469 while (argc > 0)
470 {
471 if (MatchParameter(*argv, W("?"))
472 || MatchParameter(*argv, W("help")))
473 {
474 PrintUsageHelper();
475 exit(INVALID_ARGUMENTS);
476 }
477 else if (MatchParameter(*argv, W("nologo")))
478 {
479 fDisplayLogo = false;
480 }
481 else if (MatchParameter(*argv, W("silent")))
482 {
483 dwFlags |= NGENWORKER_FLAGS_SILENT;
484 }
485 else if (MatchParameter(*argv, W("verbose")))
486 {
487 dwFlags |= NGENWORKER_FLAGS_VERBOSE;
488 }
489 else if (MatchParameter(*argv, W("Tuning")))
490 {
491 dwFlags |= NGENWORKER_FLAGS_TUNING;
492 }
493 else if (MatchParameter(*argv, W("MissingDependenciesOK")))
494 {
495 dwFlags |= NGENWORKER_FLAGS_MISSINGDEPENDENCIESOK;
496 }
497#if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
498 else if (MatchParameter(*argv, W("JITPath")) && (argc > 1))
499 {
500 pwszCLRJITPath = argv[1];
501
502 // skip JIT Path
503 argv++;
504 argc--;
505 }
506#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
507#ifdef FEATURE_WINMD_RESILIENT
508 else if (MatchParameter(*argv, W("WinMDResilient")))
509 {
510 dwFlags |= NGENWORKER_FLAGS_WINMD_RESILIENT;
511 }
512#endif
513#ifdef FEATURE_READYTORUN_COMPILER
514 else if (MatchParameter(*argv, W("ReadyToRun")))
515 {
516 dwFlags |= NGENWORKER_FLAGS_READYTORUN;
517 fExplicitReadyToRunSwitch = true;
518 }
519 else if (MatchParameter(*argv, W("FragileNonVersionable")))
520 {
521 dwFlags &= ~NGENWORKER_FLAGS_READYTORUN;
522 }
523#endif
524 else if (MatchParameter(*argv, W("NoMetaData")))
525 {
526 dwFlags |= NGENWORKER_FLAGS_NO_METADATA;
527 }
528 else if (MatchParameter(*argv, W("out")))
529 {
530 if (pwzOutputFilename != NULL)
531 {
532 Output(W("Cannot specify multiple output files.\n"));
533 exit(INVALID_ARGUMENTS);
534 }
535 pwzOutputFilename = argv[1];
536 argv++;
537 argc--;
538 }
539 else if (MatchParameter(*argv, W("in")))
540 {
541 if (pwzFilename != NULL)
542 {
543 Output(W("Cannot specify multiple input files.\n"));
544 exit(INVALID_ARGUMENTS);
545 }
546 pwzFilename = argv[1];
547 argv++;
548 argc--;
549 }
550 else if (MatchParameter(*argv, W("Trusted_Platform_Assemblies")) && (argc > 1))
551 {
552 pwzTrustedPlatformAssemblies = argv[1];
553
554 // skip path list
555 argv++;
556 argc--;
557 }
558 else if (MatchParameter(*argv, W("Platform_Resource_Roots")) && (argc > 1))
559 {
560 pwzPlatformResourceRoots = argv[1];
561
562 // skip path list
563 argv++;
564 argc--;
565 }
566 else if (MatchParameter(*argv, W("App_Paths")) && (argc > 1))
567 {
568 pwzAppPaths = argv[1];
569
570 // skip User app path
571 argv++;
572 argc--;
573 }
574#ifndef NO_NGENPDB
575 else if (MatchParameter(*argv, W("App_Ni_Paths")) && (argc > 1))
576 {
577 pwzAppNiPaths = argv[1];
578
579 // skip User app path
580 argv++;
581 argc--;
582 }
583#endif // NO_NGENPDB
584 else if (MatchParameter(*argv, W("Platform_Assemblies_Paths")) && (argc > 1))
585 {
586 pwzPlatformAssembliesPaths = argv[1];
587
588 // skip path list
589 argv++;
590 argc--;
591 }
592#ifdef FEATURE_COMINTEROP
593 else if (MatchParameter(*argv, W("Platform_Winmd_Paths")) && (argc > 1))
594 {
595 pwzPlatformWinmdPaths = argv[1];
596
597 // skip User app path
598 argv++;
599 argc--;
600 }
601#endif // FEATURE_COMINTEROP
602#ifndef NO_NGENPDB
603 else if (MatchParameter(*argv, W("CreatePDB")) && (argc > 1))
604 {
605 // syntax: /CreatePDB <directory to store PDB> [/lines [<search path for managed PDB>] ]
606
607 // Parse: /CreatePDB
608 fCreatePDB = true;
609 argv++;
610 argc--;
611
612 // Clear any extra flags - using /CreatePDB fails if any of these are set.
613 dwFlags = dwFlags & ~NGENWORKER_FLAGS_READYTORUN;
614
615 // Parse: <directory to store PDB>
616 wzDirectoryToStorePDB.Set(argv[0]);
617 argv++;
618 argc--;
619
620 // Ensure output dir ends in a backslash, or else diasymreader has issues
621 if (wzDirectoryToStorePDB[wzDirectoryToStorePDB.GetCount()-1] != DIRECTORY_SEPARATOR_CHAR_W)
622 {
623 wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W);
624 }
625
626 if (argc == 0)
627 {
628 Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n"));
629 exit(FAILURE_RESULT);
630 }
631
632 // [/lines [<search path for managed PDB>] ]
633 if (MatchParameter(*argv, W("lines")) && (argc > 1))
634 {
635 // Parse: /lines
636 fGeneratePDBLinesInfo = true;
637 argv++;
638 argc--;
639
640 if (argc == 0)
641 {
642 Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n"));
643 exit(FAILURE_RESULT);
644 }
645
646 if (argc > 1)
647 {
648 // Parse: <search path for managed PDB>
649 pwzSearchPathForManagedPDB = argv[0];
650 argv++;
651 argc--;
652 }
653 }
654
655 // Undo last arg iteration, since we do it for all cases at the bottom of
656 // the loop
657 argv--;
658 argc++;
659 }
660 else if (MatchParameter(*argv, W("DiasymreaderPath")) && (argc > 1))
661 {
662 pwzDiasymreaderPath = argv[1];
663
664 // skip diasymreader Path
665 argv++;
666 argc--;
667 }
668#endif // NO_NGENPDB
669#ifdef FEATURE_PERFMAP
670 else if (MatchParameter(*argv, W("CreatePerfMap")) && (argc > 1))
671 {
672 // syntax: /CreatePerfMap <directory to store perfmap>
673
674 // Parse: /CreatePerfMap
675 // NOTE: We use the same underlying PDB logic.
676 fCreatePDB = true;
677 argv++;
678 argc--;
679
680 // Clear the /ready to run flag - /CreatePerfmap does not work with any other flags.
681 dwFlags = dwFlags & ~NGENWORKER_FLAGS_READYTORUN;
682
683 // Parse: <directory to store PDB>
684 wzDirectoryToStorePDB.Set(argv[0]);
685 argv++;
686 argc--;
687
688 // Ensure output dir ends in a backslash
689 if (wzDirectoryToStorePDB[wcslen(wzDirectoryToStorePDB)-1] != DIRECTORY_SEPARATOR_CHAR_W)
690 {
691 wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W);
692 }
693
694 if (argc == 0)
695 {
696 Output(W("The /CreatePerfMap switch requires <directory to store perfmap> and <assembly name>.\n"));
697 exit(FAILURE_RESULT);
698 }
699
700 // Undo last arg iteration, since we do it for all cases at the bottom of
701 // the loop
702 argv--;
703 argc++;
704 }
705#endif // FEATURE_PERFMAP
706 else
707 {
708 if (argc == 1)
709 {
710#if !defined(FEATURE_PAL)
711 // When not running on Mac, which can have forward-slash pathnames, we know
712 // a command switch here means an invalid argument.
713 if (*argv[0] == W('-') || *argv[0] == W('/'))
714 {
715 Outputf(W("Invalid parameter: %s\n"), *argv);
716 exit(INVALID_ARGUMENTS);
717 }
718#endif //!FEATURE_PAL
719 // The last thing on the command line is an assembly name or path, and
720 // because we got this far is not an argument like /nologo. Because this
721 // code works on Mac, with forward-slash pathnames, we can't assume
722 // anything with a forward slash is an argument. So we just always
723 // assume the last thing on the command line must be an assembly name.
724
725 if (pwzFilename != NULL)
726 {
727 Output(W("Cannot use /In and specify an input file as the last argument.\n"));
728 exit(INVALID_ARGUMENTS);
729 }
730
731 pwzFilename = *argv;
732 break;
733 }
734 else
735 {
736 Outputf(W("Invalid parameter: %s\n"), *argv);
737 exit(INVALID_ARGUMENTS);
738 }
739 }
740
741 argv++;
742 argc--;
743 }
744
745 if (pwzFilename == NULL)
746 {
747 Output(W("You must specify an assembly to compile\n"));
748 exit(INVALID_ARGUMENTS);
749 }
750
751 if (fCreatePDB && (dwFlags != 0))
752 {
753 Output(W("The /CreatePDB switch cannot be used with other switches, except /lines and the various path switches.\n"));
754 exit(FAILURE_RESULT);
755 }
756
757 if (pwzAppNiPaths != nullptr && !fCreatePDB)
758 {
759 Output(W("The /App_Ni_Paths switch can only be used with the /CreatePDB switch.\n"));
760 exit(FAILURE_RESULT);
761 }
762
763#if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
764 if (pwszCLRJITPath != nullptr && fCreatePDB)
765 {
766 Output(W("The /JITPath switch can not be used with the /CreatePDB switch.\n"));
767 exit(FAILURE_RESULT);
768 }
769#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
770
771#if !defined(NO_NGENPDB)
772 if (pwzDiasymreaderPath != nullptr && !fCreatePDB)
773 {
774 Output(W("The /DiasymreaderPath switch can only be used with the /CreatePDB switch.\n"));
775 exit(FAILURE_RESULT);
776 }
777#endif // !defined(NO_NGENPDB)
778
779 if ((pwzTrustedPlatformAssemblies != nullptr) && (pwzPlatformAssembliesPaths != nullptr))
780 {
781 Output(W("The /Trusted_Platform_Assemblies and /Platform_Assemblies_Paths switches cannot be both specified.\n"));
782 exit(FAILURE_RESULT);
783 }
784
785 if ((dwFlags & NGENWORKER_FLAGS_NO_METADATA) != 0)
786 {
787 const size_t windowsDotWinmdLength = 13; // Length of string "Windows.winmd"
788 size_t filenameLength = wcslen(pwzFilename);
789 bool isWindowsDotWinmd = true;
790 if (filenameLength < windowsDotWinmdLength ||
791 _wcsicmp(pwzFilename + filenameLength - windowsDotWinmdLength, W("windows.winmd")) != 0)
792 {
793 isWindowsDotWinmd = false;
794 }
795 else if (filenameLength > windowsDotWinmdLength)
796 {
797 WCHAR pathSeparator = pwzFilename[filenameLength - windowsDotWinmdLength - 1];
798 if (pathSeparator != W('\\') && pathSeparator != W('/') && pathSeparator != W(':'))
799 {
800 isWindowsDotWinmd = false;
801 }
802 }
803 if (!isWindowsDotWinmd)
804 {
805 Output(W("The /NoMetaData switch can only be used with Windows.winmd.\n"));
806 exit(FAILURE_RESULT);
807 }
808 }
809
810 // All argument processing has happened by now. The only messages that should appear before here are errors
811 // related to argument parsing, such as the Usage message. Afterwards, other messages can appear.
812
813 /////////////////////////////////////////////////////////////////////////
814 //
815 // Start processing
816 //
817
818 if (fDisplayLogo)
819 {
820 PrintLogoHelper();
821 }
822
823 PathString wzTrustedPathRoot;
824
825 SString ssTPAList;
826
827 if (fCreatePDB)
828 {
829 // While creating PDB, assembly binder gives preference to files in TPA.
830 // This can create difficulties if the input file is not in TPA.
831 // To avoid this issue, put the input file as the first item in TPA.
832 ssTPAList.Append(pwzFilename);
833 }
834
835 // Are we compiling mscorlib.dll?
836 bool fCompilingMscorlib = StringEndsWith((LPWSTR)pwzFilename, CoreLibName_IL_W);
837
838// Disable fragile NGen when compiling Mscorlib for ARM.
839#if !(defined(_TARGET_ARM_) || defined(_TARGET_ARM64_))
840 if (fCompilingMscorlib && !fExplicitReadyToRunSwitch)
841 dwFlags &= ~NGENWORKER_FLAGS_READYTORUN;
842#endif // !(_TARGET_ARM_ || _TARGET_ARM64_)
843
844 if(pwzPlatformAssembliesPaths != nullptr)
845 {
846 // Platform_Assemblies_Paths command line switch has been specified.
847 _ASSERTE(pwzTrustedPlatformAssemblies == nullptr);
848
849 // Formulate the TPAList from Platform_Assemblies_Paths
850 ComputeTPAListFromPlatformAssembliesPath(pwzPlatformAssembliesPaths, ssTPAList, fCompilingMscorlib, fCreatePDB);
851 pwzTrustedPlatformAssemblies = (WCHAR *)ssTPAList.GetUnicode();
852 pwzPlatformAssembliesPaths = NULL;
853 }
854
855 if (pwzTrustedPlatformAssemblies != nullptr)
856 {
857 if (ComputeMscorlibPathFromTrustedPlatformAssemblies(wzTrustedPathRoot, pwzTrustedPlatformAssemblies))
858 {
859 pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode();
860 SetMscorlibPath(pwzPlatformAssembliesPaths);
861 }
862 }
863
864 if (pwzPlatformAssembliesPaths == NULL)
865 {
866 if (!WszGetModuleFileName(NULL, wzTrustedPathRoot))
867 {
868 ERROR_WIN32(W("Error: GetModuleFileName failed (%d)\n"), GetLastError());
869 exit(CLR_INIT_ERROR);
870 }
871
872 if (SUCCEEDED(CopySystemDirectory(wzTrustedPathRoot, wzTrustedPathRoot)))
873 {
874 pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode();
875 }
876 else
877 {
878 ERROR_HR(W("Error: wcsrchr returned NULL; GetModuleFileName must have given us something bad\n"), E_UNEXPECTED);
879 exit(CLR_INIT_ERROR);
880 }
881
882
883 }
884
885 // Initialize the logger
886 SetSvcLogger(&g_CrossgenLogger);
887
888 //Step - Compile the assembly
889
890 if (fCreatePDB)
891 {
892 hr = CreatePDBWorker(
893 pwzFilename,
894 pwzPlatformAssembliesPaths,
895 pwzTrustedPlatformAssemblies,
896 pwzPlatformResourceRoots,
897 pwzAppPaths,
898 pwzAppNiPaths,
899 wzDirectoryToStorePDB,
900 fGeneratePDBLinesInfo,
901 pwzSearchPathForManagedPDB,
902 pwzPlatformWinmdPaths,
903 pwzDiasymreaderPath);
904
905 }
906 else
907 {
908 hr = NGenWorker(pwzFilename, dwFlags,
909 pwzPlatformAssembliesPaths,
910 pwzTrustedPlatformAssemblies,
911 pwzPlatformResourceRoots,
912 pwzAppPaths,
913 pwzOutputFilename,
914 pwzPlatformWinmdPaths
915#if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
916 ,
917 NULL, // ICorSvcLogger
918 pwszCLRJITPath
919#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
920 );
921 }
922
923
924 if (FAILED(hr))
925 {
926 if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
927 {
928 OutputErrf(W("Error: file \"%s\" or one of its dependencies was not found\n"), pwzFilename);
929 exit(ASSEMBLY_NOT_FOUND);
930 }
931 else
932 {
933 OutputErrf(W("Error: compilation failed for \"%s\" (0x%08x)\n"), pwzFilename, hr);
934 exit(hr);
935 }
936 }
937
938 return 0;
939}
940
941#ifdef PLATFORM_UNIX
942int main(int argc, char *argv[])
943{
944 if (0 != PAL_Initialize(argc, argv))
945 {
946 return FAILURE_RESULT;
947 }
948
949 wchar_t **wargv = new wchar_t*[argc];
950 for (int i = 0; i < argc; i++)
951 {
952 size_t len = strlen(argv[i]) + 1;
953 wargv[i] = new wchar_t[len];
954 WszMultiByteToWideChar(CP_ACP, 0, argv[i], -1, wargv[i], len);
955 }
956
957 int ret = wmain(argc, wargv);
958
959 for (int i = 0; i < argc; i++)
960 {
961 delete[] wargv[i];
962 }
963 delete[] wargv;
964
965 return ret;
966}
967#endif // PLATFORM_UNIX
968