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.
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.
23 #include "consoleargs.h"
25 // Return values from wmain() in case of error
30 ASSEMBLY_NOT_FOUND = -3,
31 INVALID_ARGUMENTS = -4
34 #define NumItems(s) (sizeof(s) / sizeof(s[0]))
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);
41 /* --------------------------------------------------------------------------- *
43 * --------------------------------------------------------------------------- */
45 void Output(LPCWSTR str)
47 wprintf(W("%s"), str);
50 void Outputf(LPCWSTR szFormat, ...)
53 va_start(args, szFormat);
54 vfwprintf(stdout, szFormat, args);
58 void OutputErr(LPCWSTR str)
60 fwprintf(stderr, W("%s"), str);
63 void OutputErrf(LPCWSTR szFormat, ...)
66 va_start(args, szFormat);
67 vfwprintf(stderr, szFormat, args);
71 void ErrorHR(HRESULT hr)
73 OutputErrf(W("Error: failed to initialize CoreCLR: 0x%08x\n"), hr);
76 void ErrorWin32(DWORD err)
78 ErrorHR(HRESULT_FROM_WIN32(err));
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.
85 #define ERROR_HR(msg,hr) Outputf(msg, hr)
86 #define ERROR_WIN32(msg,err) Outputf(msg, err)
88 #define ERROR_HR(msg,hr) ErrorHR(hr)
89 #define ERROR_WIN32(msg,err) ErrorWin32(err)
93 void PrintLogoHelper()
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);
101 void PrintUsageHelper()
103 // Always print the logo when we print the usage, even if they've specified /nologo and we've parsed that already.
107 W("Usage: crossgen [args] <assembly name>\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")
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")
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")
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")
141 W(" /MissingDependenciesOK\n")
142 W(" - Specifies that crossgen should attempt not to fail\n")
143 W(" if a dependency is missing.\n")
145 W(" /Tuning - Generate an instrumented image to collect\n")
146 W(" scenario traces, which can be used with ibcmerge.exe\n")
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")
156 #ifdef FEATURE_WINMD_RESILIENT
157 W(" WinMD Parameters\n")
158 W(" /WinMDResilient - Generate images resilient to WinMD dependency changes.\n")
160 W(" Size on Disk Parameters\n")
161 W(" /NoMetaData - Do not copy metadata and IL into native image.\n")
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")
178 class CrossgenLogger : public ICorSvcLogger
180 STDMETHODIMP_(ULONG) AddRef() {return E_NOTIMPL;}
181 STDMETHODIMP_(ULONG) Release() {return E_NOTIMPL;}
182 STDMETHODIMP QueryInterface(REFIID riid,void ** ppv)
189 if (IsEqualIID(riid, IID_ICorSvcLogger) || IsEqualIID(riid, IID_IUnknown))
196 return E_NOINTERFACE;
200 HRESULT STDMETHODCALLTYPE Log(
201 /*[in] */CorSvcLogLevel logLevel,
202 /*[in] */BSTR message
205 if (logLevel == LogLevel_Error)
213 CrossgenLogger g_CrossgenLogger;
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.
219 bool MatchParameter(LPCWSTR szArg, LPCWSTR szTestParamName)
221 if (wcslen(szArg) == 0)
226 if (szArg[0] != W('/') && szArg[0] != W('-'))
231 return !_wcsicmp(szArg + 1, szTestParamName) || !_wcsicmp(szArg + 1, szTestParamName);
235 // Returns true if pwzString ends with the string in pwzCandidate
238 bool StringEndsWith(LPCWSTR pwzString, LPCWSTR pwzCandidate)
240 size_t stringLength = wcslen(pwzString);
241 size_t candidateLength = wcslen(pwzCandidate);
243 if (candidateLength > stringLength || stringLength == 0 || candidateLength == 0)
248 LPCWSTR pwzStringEnd = pwzString + stringLength - candidateLength;
250 return !_wcsicmp(pwzStringEnd, pwzCandidate);
254 // When using the Phone binding model (TrustedPlatformAssemblies), automatically
255 // detect which path CoreLib.[ni.]dll lies in.
257 bool ComputeMscorlibPathFromTrustedPlatformAssemblies(SString& pwzMscorlibPath, LPCWSTR pwzTrustedPlatformAssemblies)
259 LPWSTR wszTrustedPathCopy = new WCHAR[wcslen(pwzTrustedPlatformAssemblies) + 1];
260 wcscpy_s(wszTrustedPathCopy, wcslen(pwzTrustedPlatformAssemblies) + 1, pwzTrustedPlatformAssemblies);
262 LPWSTR wszSingleTrustedPath = wcstok_s(wszTrustedPathCopy, PATH_SEPARATOR_STR_W, &context);
264 while (wszSingleTrustedPath != NULL)
266 size_t pathLength = wcslen(wszSingleTrustedPath);
267 // Strip off enclosing quotes, if present
268 if (wszSingleTrustedPath[0] == W('\"') && wszSingleTrustedPath[pathLength-1] == W('\"'))
270 wszSingleTrustedPath[pathLength-1] = '\0';
271 wszSingleTrustedPath++;
274 if (StringEndsWith(wszSingleTrustedPath, DIRECTORY_SEPARATOR_STR_W CoreLibName_IL_W) ||
275 StringEndsWith(wszSingleTrustedPath, DIRECTORY_SEPARATOR_STR_W CoreLibName_NI_W))
277 pwzMscorlibPath.Set(wszSingleTrustedPath);
278 SString::Iterator pwzSeparator = pwzMscorlibPath.End();
281 if (!SUCCEEDED(CopySystemDirectory(pwzMscorlibPath, pwzMscorlibPath)))
286 delete [] wszTrustedPathCopy;
290 wszSingleTrustedPath = wcstok_s(NULL, PATH_SEPARATOR_STR_W, &context);
292 delete [] wszTrustedPathCopy;
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
300 void PopulateTPAList(SString path, LPCWSTR pwszMask, SString &refTPAList, bool fCompilingMscorlib, bool fCreatePDB)
302 _ASSERTE(path.GetCount() > 0);
303 ClrDirectoryEnumerator folderEnumerator(path.GetUnicode(), pwszMask);
305 while (folderEnumerator.Next())
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)))
311 bool fAddDelimiter = (refTPAList.GetCount() > 0)?true:false;
312 bool fAddFileToTPAList = true;
313 LPCWSTR pwszFilename = folderEnumerator.GetFileName();
314 if (fCompilingMscorlib)
316 // When compiling CoreLib, no ".ni.dll" should be on the TPAList.
317 if (StringEndsWith((LPWSTR)pwszFilename, W(".ni.dll")))
319 fAddFileToTPAList = false;
324 // When creating PDBs, we must ensure that .ni.dlls are in the TPAList
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")))
330 if (!StringEndsWith((LPWSTR)pwszFilename, CoreLibName_NI_W))
332 fAddFileToTPAList = false;
337 // Ensure that CoreLib's IL version is also not on the TPAlist for this case.
338 if (StringEndsWith((LPWSTR)pwszFilename, CoreLibName_IL_W))
340 fAddFileToTPAList = false;
344 if (fAddFileToTPAList)
348 // Add the path delimiter if we already have entries in the TPAList
349 refTPAList.Append(PATH_SEPARATOR_CHAR_W);
351 // Add the path to the TPAList
352 refTPAList.Append(path);
353 refTPAList.Append(pwszFilename);
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)
363 // We should have a valid pointer to the paths
364 _ASSERTE(pwzPlatformAssembliesPaths != NULL);
366 SString ssPlatformAssembliesPath(pwzPlatformAssembliesPaths);
368 // Platform Assemblies Path List is semi-colon delimited
369 if(ssPlatformAssembliesPath.GetCount() > 0)
371 SString::CIterator start = ssPlatformAssembliesPath.Begin();
372 SString::CIterator itr = ssPlatformAssembliesPath.Begin();
373 SString::CIterator end = ssPlatformAssembliesPath.End();
374 SString qualifiedPath;
379 BOOL found = ssPlatformAssembliesPath.Find(itr, PATH_SEPARATOR_CHAR_W);
385 SString qualifiedPath(ssPlatformAssembliesPath,start,itr);
392 unsigned len = qualifiedPath.GetCount();
396 if (qualifiedPath[len-1]!=DIRECTORY_SEPARATOR_CHAR_W)
398 qualifiedPath.Append(DIRECTORY_SEPARATOR_CHAR_W);
401 // Enumerate the EXE/DLL modules within this path and add them to the TPAList
404 PopulateTPAList(qualifiedPath, W("*.exe"), refTPAList, fCompilingMscorlib, fCreatePDB);
405 PopulateTPAList(qualifiedPath, W("*.dll"), refTPAList, fCompilingMscorlib, fCreatePDB);
409 Outputf(W("Warning: Error enumerating files under %s.\n"), qualifiedPath.GetUnicode());
411 EX_END_CATCH(SwallowAllExceptions);
417 extern HMODULE g_hThisInst;
419 int _cdecl wmain(int argc, __in_ecount(argc) WCHAR **argv)
422 g_hThisInst = WszGetModuleHandle(NULL);
425 /////////////////////////////////////////////////////////////////////////
427 // Parse the arguments
429 bool fDisplayLogo = true;
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;
445 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
446 LPCWSTR pwszCLRJITPath = nullptr;
447 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
449 LPCWSTR pwzDiasymreaderPath = nullptr;
453 #ifndef PLATFORM_UNIX
454 // This is required to properly display Unicode characters
455 _setmode(_fileno(stdout), _O_U8TEXT);
458 // Skip this executable path
462 ConsoleArgs consoleArgs;
469 exit(INVALID_ARGUMENTS);
472 if (!consoleArgs.ExpandResponseFiles(argc, argv, &argc2, &argv2))
474 if (consoleArgs.ErrorMessage() != nullptr)
476 wprintf(consoleArgs.ErrorMessage());
477 exit(FAILURE_RESULT);
484 // By default, Crossgen will assume code-generation for fulltrust domains unless /PartialTrust switch is specified
485 dwFlags |= NGENWORKER_FLAGS_FULLTRUSTDOMAIN;
487 // By default, Crossgen will generate readytorun images unless /FragileNonVersionable switch is specified
488 dwFlags |= NGENWORKER_FLAGS_READYTORUN;
492 if (MatchParameter(*argv, W("?"))
493 || MatchParameter(*argv, W("help")))
496 exit(INVALID_ARGUMENTS);
498 else if (MatchParameter(*argv, W("nologo")))
500 fDisplayLogo = false;
502 else if (MatchParameter(*argv, W("silent")))
504 dwFlags |= NGENWORKER_FLAGS_SILENT;
506 else if (MatchParameter(*argv, W("Tuning")))
508 dwFlags |= NGENWORKER_FLAGS_TUNING;
510 else if (MatchParameter(*argv, W("MissingDependenciesOK")))
512 dwFlags |= NGENWORKER_FLAGS_MISSINGDEPENDENCIESOK;
514 else if (MatchParameter(*argv, W("PartialTrust")))
516 // Clear the /fulltrust flag
517 dwFlags = dwFlags & ~NGENWORKER_FLAGS_FULLTRUSTDOMAIN;
519 else if (MatchParameter(*argv, W("FullTrust")))
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.
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.
528 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
529 else if (MatchParameter(*argv, W("JITPath")) && (argc > 1))
531 pwszCLRJITPath = argv[1];
537 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
538 #ifdef FEATURE_WINMD_RESILIENT
539 else if (MatchParameter(*argv, W("WinMDResilient")))
541 dwFlags |= NGENWORKER_FLAGS_WINMD_RESILIENT;
544 #ifdef FEATURE_READYTORUN_COMPILER
545 else if (MatchParameter(*argv, W("ReadyToRun")))
547 dwFlags |= NGENWORKER_FLAGS_READYTORUN;
549 else if (MatchParameter(*argv, W("FragileNonVersionable")))
551 dwFlags &= ~NGENWORKER_FLAGS_READYTORUN;
554 else if (MatchParameter(*argv, W("NoMetaData")))
556 dwFlags |= NGENWORKER_FLAGS_NO_METADATA;
558 else if (MatchParameter(*argv, W("out")))
560 if (pwzOutputFilename != NULL)
562 Output(W("Cannot specify multiple output files.\n"));
563 exit(INVALID_ARGUMENTS);
565 pwzOutputFilename = argv[1];
569 else if (MatchParameter(*argv, W("in")))
571 if (pwzFilename != NULL)
573 Output(W("Cannot specify multiple input files.\n"));
574 exit(INVALID_ARGUMENTS);
576 pwzFilename = argv[1];
580 else if (MatchParameter(*argv, W("Trusted_Platform_Assemblies")) && (argc > 1))
582 pwzTrustedPlatformAssemblies = argv[1];
588 else if (MatchParameter(*argv, W("Platform_Resource_Roots")) && (argc > 1))
590 pwzPlatformResourceRoots = argv[1];
596 else if (MatchParameter(*argv, W("App_Paths")) && (argc > 1))
598 pwzAppPaths = argv[1];
600 // skip User app path
605 else if (MatchParameter(*argv, W("App_Ni_Paths")) && (argc > 1))
607 pwzAppNiPaths = argv[1];
609 // skip User app path
614 else if (MatchParameter(*argv, W("Platform_Assemblies_Paths")) && (argc > 1))
616 pwzPlatformAssembliesPaths = argv[1];
622 #ifdef FEATURE_COMINTEROP
623 else if (MatchParameter(*argv, W("Platform_Winmd_Paths")) && (argc > 1))
625 pwzPlatformWinmdPaths = argv[1];
627 // skip User app path
631 #endif // FEATURE_COMINTEROP
633 else if (MatchParameter(*argv, W("CreatePDB")) && (argc > 1))
635 // syntax: /CreatePDB <directory to store PDB> [/lines [<search path for managed PDB>] ]
642 // Clear the /fulltrust flag - /CreatePDB does not work with any other flags.
643 dwFlags = dwFlags & ~(NGENWORKER_FLAGS_FULLTRUSTDOMAIN | NGENWORKER_FLAGS_READYTORUN);
645 // Parse: <directory to store PDB>
646 wzDirectoryToStorePDB.Set(argv[0]);
650 // Ensure output dir ends in a backslash, or else diasymreader has issues
651 if (wzDirectoryToStorePDB[wzDirectoryToStorePDB.GetCount()-1] != DIRECTORY_SEPARATOR_CHAR_W)
653 wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W);
658 Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n"));
659 exit(FAILURE_RESULT);
662 // [/lines [<search path for managed PDB>] ]
663 if (MatchParameter(*argv, W("lines")) && (argc > 1))
666 fGeneratePDBLinesInfo = true;
672 Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n"));
673 exit(FAILURE_RESULT);
678 // Parse: <search path for managed PDB>
679 pwzSearchPathForManagedPDB = argv[0];
685 // Undo last arg iteration, since we do it for all cases at the bottom of
690 else if (MatchParameter(*argv, W("DiasymreaderPath")) && (argc > 1))
692 pwzDiasymreaderPath = argv[1];
694 // skip diasymreader Path
699 #ifdef FEATURE_PERFMAP
700 else if (MatchParameter(*argv, W("CreatePerfMap")) && (argc > 1))
702 // syntax: /CreatePerfMap <directory to store perfmap>
704 // Parse: /CreatePerfMap
705 // NOTE: We use the same underlying PDB logic.
710 // Clear the /fulltrust flag - /CreatePerfMap does not work with any other flags.
711 dwFlags = dwFlags & ~NGENWORKER_FLAGS_FULLTRUSTDOMAIN;
713 // Clear the /ready to run flag - /CreatePerfmap does not work with any other flags.
714 dwFlags = dwFlags & ~NGENWORKER_FLAGS_READYTORUN;
716 // Parse: <directory to store PDB>
717 wzDirectoryToStorePDB.Set(argv[0]);
721 // Ensure output dir ends in a backslash
722 if (wzDirectoryToStorePDB[wcslen(wzDirectoryToStorePDB)-1] != DIRECTORY_SEPARATOR_CHAR_W)
724 wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W);
729 Output(W("The /CreatePerfMap switch requires <directory to store perfmap> and <assembly name>.\n"));
730 exit(FAILURE_RESULT);
733 // Undo last arg iteration, since we do it for all cases at the bottom of
738 #endif // FEATURE_PERFMAP
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('/'))
748 Outputf(W("Invalid parameter: %s\n"), *argv);
749 exit(INVALID_ARGUMENTS);
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.
758 if (pwzFilename != NULL)
760 Output(W("Cannot use /In and specify an input file as the last argument.\n"));
761 exit(INVALID_ARGUMENTS);
769 Outputf(W("Invalid parameter: %s\n"), *argv);
770 exit(INVALID_ARGUMENTS);
778 if (pwzFilename == NULL)
780 Output(W("You must specify an assembly to compile\n"));
781 exit(INVALID_ARGUMENTS);
784 if (fCreatePDB && (dwFlags != 0))
786 Output(W("The /CreatePDB switch cannot be used with other switches, except /lines and the various path switches.\n"));
787 exit(FAILURE_RESULT);
790 if (pwzAppNiPaths != nullptr && !fCreatePDB)
792 Output(W("The /App_Ni_Paths switch can only be used with the /CreatePDB switch.\n"));
793 exit(FAILURE_RESULT);
796 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
797 if (pwszCLRJITPath != nullptr && fCreatePDB)
799 Output(W("The /JITPath switch can not be used with the /CreatePDB switch.\n"));
800 exit(FAILURE_RESULT);
802 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
804 #if !defined(NO_NGENPDB)
805 if (pwzDiasymreaderPath != nullptr && !fCreatePDB)
807 Output(W("The /DiasymreaderPath switch can only be used with the /CreatePDB switch.\n"));
808 exit(FAILURE_RESULT);
810 #endif // !defined(NO_NGENPDB)
812 if ((pwzTrustedPlatformAssemblies != nullptr) && (pwzPlatformAssembliesPaths != nullptr))
814 Output(W("The /Trusted_Platform_Assemblies and /Platform_Assemblies_Paths switches cannot be both specified.\n"));
815 exit(FAILURE_RESULT);
818 if ((dwFlags & NGENWORKER_FLAGS_NO_METADATA) != 0)
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)
826 isWindowsDotWinmd = false;
828 else if (filenameLength > windowsDotWinmdLength)
830 WCHAR pathSeparator = pwzFilename[filenameLength - windowsDotWinmdLength - 1];
831 if (pathSeparator != W('\\') && pathSeparator != W('/') && pathSeparator != W(':'))
833 isWindowsDotWinmd = false;
836 if (!isWindowsDotWinmd)
838 Output(W("The /NoMetaData switch can only be used with Windows.winmd.\n"));
839 exit(FAILURE_RESULT);
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.
846 /////////////////////////////////////////////////////////////////////////
856 PathString wzTrustedPathRoot;
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);
868 // Are we compiling mscorlib.dll?
869 bool fCompilingMscorlib = StringEndsWith((LPWSTR)pwzFilename, CoreLibName_IL_W);
871 if (fCompilingMscorlib)
872 dwFlags &= ~NGENWORKER_FLAGS_READYTORUN;
874 if(pwzPlatformAssembliesPaths != nullptr)
876 // Platform_Assemblies_Paths command line switch has been specified.
877 _ASSERTE(pwzTrustedPlatformAssemblies == nullptr);
879 // Formulate the TPAList from Platform_Assemblies_Paths
880 ComputeTPAListFromPlatformAssembliesPath(pwzPlatformAssembliesPaths, ssTPAList, fCompilingMscorlib, fCreatePDB);
881 pwzTrustedPlatformAssemblies = (WCHAR *)ssTPAList.GetUnicode();
882 pwzPlatformAssembliesPaths = NULL;
885 if (pwzTrustedPlatformAssemblies != nullptr)
887 if (ComputeMscorlibPathFromTrustedPlatformAssemblies(wzTrustedPathRoot, pwzTrustedPlatformAssemblies))
889 pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode();
890 SetMscorlibPath(pwzPlatformAssembliesPaths);
894 if (pwzPlatformAssembliesPaths == NULL)
896 if (!WszGetModuleFileName(NULL, wzTrustedPathRoot))
898 ERROR_WIN32(W("Error: GetModuleFileName failed (%d)\n"), GetLastError());
899 exit(CLR_INIT_ERROR);
902 if (SUCCEEDED(CopySystemDirectory(wzTrustedPathRoot, wzTrustedPathRoot)))
904 pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode();
908 ERROR_HR(W("Error: wcsrchr returned NULL; GetModuleFileName must have given us something bad\n"), E_UNEXPECTED);
909 exit(CLR_INIT_ERROR);
915 // Initialize the logger
916 SetSvcLogger(&g_CrossgenLogger);
918 //Step - Compile the assembly
922 hr = CreatePDBWorker(
924 pwzPlatformAssembliesPaths,
925 pwzTrustedPlatformAssemblies,
926 pwzPlatformResourceRoots,
929 wzDirectoryToStorePDB,
930 fGeneratePDBLinesInfo,
931 pwzSearchPathForManagedPDB,
932 pwzPlatformWinmdPaths,
933 pwzDiasymreaderPath);
938 hr = NGenWorker(pwzFilename, dwFlags,
939 pwzPlatformAssembliesPaths,
940 pwzTrustedPlatformAssemblies,
941 pwzPlatformResourceRoots,
944 pwzPlatformWinmdPaths
945 #if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
947 NULL, // ICorSvcLogger
949 #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
956 if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
958 OutputErrf(W("Error: file \"%s\" or one of its dependencies was not found\n"), pwzFilename);
959 exit(ASSEMBLY_NOT_FOUND);
963 OutputErrf(W("Error: compilation failed for \"%s\" (0x%08x)\n"), pwzFilename, hr);
972 int main(int argc, char *argv[])
974 if (0 != PAL_Initialize(argc, argv))
976 return FAILURE_RESULT;
979 wchar_t **wargv = new wchar_t*[argc];
980 for (int i = 0; i < argc; i++)
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);
987 int ret = wmain(argc, wargv);
989 for (int i = 0; i < argc; i++)
997 #endif // PLATFORM_UNIX