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 | 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 |
942 | int 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 | |