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