diff --git a/makefile.shade b/makefile.shade index 03882fa24..c52e3ee3d 100644 --- a/makefile.shade +++ b/makefile.shade @@ -364,7 +364,6 @@ var CAN_BUILD_ONECORE = '${Directory.Exists(WIN10_SDK_LIB) && Directory.GetFiles Path.Combine("src", BOOTSTRAPPER_FOLDER_NAME, "pal.unix.cpp"), Path.Combine("src", BOOTSTRAPPER_FOLDER_NAME, "pal.linux.cpp"), Path.Combine("src", BOOTSTRAPPER_COMMON_FOLDER_NAME, "utils.cpp"), - Path.Combine("src", BOOTSTRAPPER_FOLDER_NAME, "tchar.cpp"), }; Directory.CreateDirectory(soOutputDir); @@ -405,7 +404,6 @@ var CAN_BUILD_ONECORE = '${Directory.Exists(WIN10_SDK_LIB) && Directory.GetFiles Path.Combine("src", BOOTSTRAPPER_FOLDER_NAME, "pal.unix.cpp"), Path.Combine("src", BOOTSTRAPPER_FOLDER_NAME, "pal.darwin.cpp"), Path.Combine("src", BOOTSTRAPPER_COMMON_FOLDER_NAME, "utils.cpp"), - Path.Combine("src", BOOTSTRAPPER_FOLDER_NAME, "tchar.cpp"), }; Directory.CreateDirectory(soOutputDir); diff --git a/src/dnx.common/include/utils.h b/src/dnx.common/include/utils.h index b85b9ccde..1aa5f66db 100644 --- a/src/dnx.common/include/utils.h +++ b/src/dnx.common/include/utils.h @@ -16,8 +16,6 @@ namespace dnx dnx::xstring_t to_xstring_t(const std::wstring& s); std::wstring to_wstring(const std::string& s); - bool strings_equal_ignore_case(const char_t* s1, const char_t* s2); - dnx::xstring_t path_combine(const dnx::xstring_t& path1, const dnx::xstring_t& path2); bool file_exists(const dnx::xstring_t& path); dnx::xstring_t remove_file_from_path(const dnx::xstring_t& path); diff --git a/src/dnx.common/include/xplat.h b/src/dnx.common/include/xplat.h index b2d2a6232..1dd7d022f 100644 --- a/src/dnx.common/include/xplat.h +++ b/src/dnx.common/include/xplat.h @@ -18,6 +18,8 @@ typedef std::wstring xstring_t; #define PATH_SEPARATOR L"\\" +#define x_strlen wcslen + #else // non-windows typedef char char_t; @@ -27,5 +29,7 @@ typedef std::string xstring_t; #define PATH_SEPARATOR "/" +#define x_strlen strlen + #endif } diff --git a/src/dnx.common/utils.cpp b/src/dnx.common/utils.cpp index 036fd36cd..a558cd004 100644 --- a/src/dnx.common/utils.cpp +++ b/src/dnx.common/utils.cpp @@ -11,8 +11,6 @@ #if defined(_WIN32) #include #include -#else -#include #endif namespace dnx @@ -53,15 +51,6 @@ namespace dnx } #endif - bool strings_equal_ignore_case(const char_t* s1, const char_t* s2) - { -#if defined(_WIN32) - return _wcsicmp(s1, s2) == 0; -#else - return strcasecmp(s1, s2) == 0; -#endif - } - bool ends_with_slash(const xstring_t& path) { if (path.length() > 0) diff --git a/src/dnx/dnx.cpp b/src/dnx/dnx.cpp index 088a7eee7..0b665ea5e 100644 --- a/src/dnx/dnx.cpp +++ b/src/dnx/dnx.cpp @@ -2,106 +2,81 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #include "stdafx.h" +#include #include "pal.h" #include "utils.h" #include "app_main.h" -bool LastIndexOfCharInPath(LPCTSTR pszStr, TCHAR c, size_t* pIndex) +bool strings_equal_ignore_case(const dnx::char_t* s1, const dnx::char_t* s2) { - size_t nIndex = _tcsnlen(pszStr, MAX_PATH) - 1; - for (; nIndex != 0; nIndex--) - { - if (pszStr[nIndex] == c) - { - break; - } - } - - *pIndex = nIndex; - return pszStr[nIndex] == c; +#if defined(_WIN32) + return _wcsicmp(s1, s2) == 0; +#else + return strcasecmp(s1, s2) == 0; +#endif } -bool PathEndsWith(LPCTSTR pszStr, LPCTSTR pszSuffix) +bool string_ends_with_ignore_case(const dnx::char_t* s, const dnx::char_t* suffix) { - size_t nStrLen = _tcsnlen(pszStr, MAX_PATH); - size_t nSuffixLen = _tcsnlen(pszSuffix, MAX_PATH); + auto str_len = x_strlen(s); + auto suffix_len = x_strlen(suffix); - if (nSuffixLen > nStrLen) + if (suffix_len > str_len) { return false; } - size_t nOffset = nStrLen - nSuffixLen; - - return ::_tcsnicmp(pszStr + nOffset, pszSuffix, MAX_PATH - nOffset) == 0; + return strings_equal_ignore_case(s + str_len - suffix_len, suffix); } -bool LastPathSeparatorIndex(LPCTSTR pszPath, size_t* pIndex) +int split_path(const dnx::char_t* path) { - size_t nLastSlashIndex; - size_t nLastBackSlashIndex; - - bool hasLastSlashIndex = LastIndexOfCharInPath(pszPath, _T('/'), &nLastSlashIndex); - bool hasLastBackSlashIndex = LastIndexOfCharInPath(pszPath, _T('\\'), &nLastBackSlashIndex); - - if (hasLastSlashIndex && hasLastBackSlashIndex) + for (auto i = static_cast(x_strlen(path)) - 1; i >= 0; i--) { - *pIndex = max(nLastSlashIndex, nLastBackSlashIndex); - return true; - } - - if (!hasLastSlashIndex && !hasLastBackSlashIndex) - { - return false; + if (path[i] == _X('\\') || path[i] == _X('/')) + { + return i; + } } - *pIndex = hasLastSlashIndex ? nLastSlashIndex : nLastBackSlashIndex; - return true; + return -1; } -void GetParentDir(LPCTSTR pszPath, LPTSTR pszParentDir) +const dnx::char_t* allocate_and_copy(const dnx::char_t* value, size_t count) { - size_t nLastSeparatorIndex; - if (!LastPathSeparatorIndex(pszPath, &nLastSeparatorIndex)) - { - _tcscpy_s(pszParentDir, MAX_PATH, _T(".")); - return; - } - - memcpy(pszParentDir, pszPath, (nLastSeparatorIndex + 1) * sizeof(TCHAR)); - pszParentDir[nLastSeparatorIndex + 1] = _T('\0'); + auto buff_size = count + 1; + auto buffer = new dnx::char_t[buff_size]; +#if defined(_WIN32) + wcsncpy_s(buffer, buff_size, value, count); +#else + strncpy(buffer, value, count); + buffer[count] = '\0'; +#endif + return buffer; } -void GetFileName(LPCTSTR pszPath, LPTSTR pszFileName) +const dnx::char_t* allocate_and_copy(const dnx::char_t* value) { - size_t nLastSeparatorIndex; - - if (!LastPathSeparatorIndex(pszPath, &nLastSeparatorIndex)) - { - _tcscpy_s(pszFileName, MAX_PATH, pszPath); - return; - } - - _tcscpy_s(pszFileName, MAX_PATH, pszPath + nLastSeparatorIndex + 1); + return allocate_and_copy(value, x_strlen(value)); } -int BootstrapperOptionValueNum(LPCTSTR pszCandidate) +int BootstrapperOptionValueNum(const dnx::char_t* pszCandidate) { - if (dnx::utils::strings_equal_ignore_case(pszCandidate, _T("--appbase")) || - dnx::utils::strings_equal_ignore_case(pszCandidate, _T("--lib")) || - dnx::utils::strings_equal_ignore_case(pszCandidate, _T("--packages")) || - dnx::utils::strings_equal_ignore_case(pszCandidate, _T("--configuration")) || - dnx::utils::strings_equal_ignore_case(pszCandidate, _T("--port"))) + if (strings_equal_ignore_case(pszCandidate, _X("--appbase")) || + strings_equal_ignore_case(pszCandidate, _X("--lib")) || + strings_equal_ignore_case(pszCandidate, _X("--packages")) || + strings_equal_ignore_case(pszCandidate, _X("--configuration")) || + strings_equal_ignore_case(pszCandidate, _X("--port"))) { return 1; } - if (dnx::utils::strings_equal_ignore_case(pszCandidate, _T("--watch")) || - dnx::utils::strings_equal_ignore_case(pszCandidate, _T("--debug")) || - dnx::utils::strings_equal_ignore_case(pszCandidate, _T("--help")) || - dnx::utils::strings_equal_ignore_case(pszCandidate, _T("-h")) || - dnx::utils::strings_equal_ignore_case(pszCandidate, _T("-?")) || - dnx::utils::strings_equal_ignore_case(pszCandidate, _T("--version"))) + if (strings_equal_ignore_case(pszCandidate, _X("--watch")) || + strings_equal_ignore_case(pszCandidate, _X("--debug")) || + strings_equal_ignore_case(pszCandidate, _X("--help")) || + strings_equal_ignore_case(pszCandidate, _X("-h")) || + strings_equal_ignore_case(pszCandidate, _X("-?")) || + strings_equal_ignore_case(pszCandidate, _X("--version"))) { return 0; } @@ -110,125 +85,139 @@ int BootstrapperOptionValueNum(LPCTSTR pszCandidate) return -1; } -void FreeExpandedCommandLineArguments(int nArgc, dnx::char_t** ppszArgv) +size_t FindAppBaseOrNonHostOption(size_t argc, dnx::char_t**argv) { - for (int i = 0; i < nArgc; ++i) + for (size_t i = 0; i < argc; i++) { - delete[] ppszArgv[i]; - } - delete[] ppszArgv; -} + if (strings_equal_ignore_case(argv[i], _X("--appbase"))) + { + return i; + } -dnx::char_t* GetAppBaseParameterValue(int argc, dnx::char_t* argv[]) -{ - for (auto i = 0; i < argc - 1; ++i) - { - if (dnx::utils::strings_equal_ignore_case(argv[i], _X("--appbase"))) + auto option_num_args = BootstrapperOptionValueNum(argv[i]); + if (option_num_args < 0) { - return argv[i + 1]; + return i; } + + i += option_num_args; } - return nullptr; + return argc; } -bool ExpandCommandLineArguments(int nArgc, dnx::char_t** ppszArgv, int& nExpandedArgc, dnx::char_t**& ppszExpandedArgv) +dnx::char_t* GetAppBaseParameterValue(size_t argc, dnx::char_t* argv[]) { - // If no args or '--appbase' is already given and it has a value - if (nArgc == 0 || GetAppBaseParameterValue(nArgc, ppszArgv)) + auto index = FindAppBaseOrNonHostOption(argc, argv); + + // no parameters or '--appbase' is the last value in the array or `--appbase` not found + if (argc == 0 || index >= argc - 1 || argv[index][0] != _X('-')) { - return false; + return nullptr; } - nExpandedArgc = nArgc + 2; - ppszExpandedArgv = new LPTSTR[nExpandedArgc]; - memset(ppszExpandedArgv, 0, nExpandedArgc*sizeof(LPTSTR)); - TCHAR szParentDir[MAX_PATH]; - - // Copy all arguments (options & values) as is before the project.json/assembly path - int nPathArgIndex = -1; - int nOptValNum; - while (++nPathArgIndex < nArgc) - { - nOptValNum = BootstrapperOptionValueNum(ppszArgv[nPathArgIndex]); + return argv[index + 1]; +} - // It isn't a bootstrapper option, we treat it as the project.json/assembly path - if (nOptValNum < 0) - { - break; - } +void AppendAppbaseFromFile(const dnx::char_t* path, std::vector& expanded_args) +{ + auto split_idx = split_path(path); - // Copy the option - ppszExpandedArgv[nPathArgIndex] = new TCHAR[MAX_PATH]; - _tcscpy_s(ppszExpandedArgv[nPathArgIndex], MAX_PATH, ppszArgv[nPathArgIndex]); + expanded_args.push_back(allocate_and_copy(_X("--appbase"))); - // Copy the value if the option has one - if (nOptValNum > 0 && (++nPathArgIndex < nArgc)) - { - ppszExpandedArgv[nPathArgIndex] = new TCHAR[MAX_PATH]; - _tcscpy_s(ppszExpandedArgv[nPathArgIndex], MAX_PATH, ppszArgv[nPathArgIndex]); - } + if (split_idx < 0) + { + expanded_args.push_back(allocate_and_copy(_X("."))); } - - // No path argument was found, no expansion is needed - if (nPathArgIndex >= nArgc) + else { - FreeExpandedCommandLineArguments(nExpandedArgc, ppszExpandedArgv); - return false; + expanded_args.push_back(allocate_and_copy(path, split_idx + 1)); } +} - // Allocate memory before doing expansion - for (int i = nPathArgIndex; i < nExpandedArgc; ++i) +void ExpandArgument(const dnx::char_t* value, std::vector& expanded_args) +{ + if (string_ends_with_ignore_case(value, _X(".dll")) || string_ends_with_ignore_case(value, _X(".exe"))) { - ppszExpandedArgv[i] = new TCHAR[MAX_PATH]; + // "dnx /path/App.dll arg1" --> "dnx --appbase /path/ /path/App.dll arg1" + // "dnx /path/App.exe arg1" --> "dnx --appbase /path/ /path/App.exe arg1" + // "dnx App.exe arg1" --> "dnx --appbase . App.exe arg1" + AppendAppbaseFromFile(value, expanded_args); + expanded_args.push_back(allocate_and_copy(value)); + + return; } - // "dnx /path/App.dll arg1" --> "dnx --appbase /path/ /path/App.dll arg1" - // "dnx /path/App.exe arg1" --> "dnx --appbase /path/ /path/App.exe arg1" - LPTSTR pszPathArg = ppszArgv[nPathArgIndex]; - if (PathEndsWith(pszPathArg, _T(".exe")) || PathEndsWith(pszPathArg, _T(".dll"))) + if (strings_equal_ignore_case(value, _X("."))) { - GetParentDir(pszPathArg, szParentDir); - - _tcscpy_s(ppszExpandedArgv[nPathArgIndex], MAX_PATH, _T("--appbase")); - _tcscpy_s(ppszExpandedArgv[nPathArgIndex + 1], MAX_PATH, szParentDir); + // "dnx . run" --> "dnx --appbase . Microsoft.Framework.ApplicationHost run" + expanded_args.push_back(allocate_and_copy(_X("--appbase"))); + expanded_args.push_back(allocate_and_copy(value)); + expanded_args.push_back(allocate_and_copy(_X("Microsoft.Framework.ApplicationHost"))); - // Copy all arguments/options as is - for (int i = nPathArgIndex; i < nArgc; ++i) - { - _tcscpy_s(ppszExpandedArgv[i + 2], MAX_PATH, ppszArgv[i]); - } + return; + } - return true; + auto split_idx = split_path(value); + if (string_ends_with_ignore_case(value + (split_idx < 0 ? 0 : split_idx), _X("project.json"))) + { + // "dnx /path/project.json run" --> "dnx --appbase /path/ Microsoft.Framework.ApplicationHost run" + AppendAppbaseFromFile(value, expanded_args); + expanded_args.push_back(allocate_and_copy(_X("Microsoft.Framework.ApplicationHost"))); + return; } - // "dnx /path/project.json run" --> "dnx --appbase /path/ Microsoft.Framework.ApplicationHost run" - // "dnx /path/ run" --> "dnx --appbase /path/ Microsoft.Framework.ApplicationHost run" - TCHAR szFileName[MAX_PATH]; - GetFileName(pszPathArg, szFileName); - if (dnx::utils::strings_equal_ignore_case(szFileName, _T("project.json"))) + // "dnx run" --> "dnx --appbase . Microsoft.Framework.ApplicationHost run" + expanded_args.push_back(allocate_and_copy(_X("--appbase"))); + expanded_args.push_back(allocate_and_copy(_X("."))); + expanded_args.push_back(allocate_and_copy(_X("Microsoft.Framework.ApplicationHost"))); + expanded_args.push_back(allocate_and_copy(value)); +} + +bool ExpandCommandLineArguments(size_t nArgc, dnx::char_t** ppszArgv, size_t& nExpandedArgc, dnx::char_t**& ppszExpandedArgv) +{ + size_t pivot_parameter_idx = FindAppBaseOrNonHostOption(nArgc, ppszArgv); + + // either no non-bootstrapper option found or --appbase was found - in either case expansion is not needed + if (pivot_parameter_idx >= nArgc || ppszArgv[pivot_parameter_idx][0] == _X('-')) { - GetParentDir(pszPathArg, szParentDir); + return false; } - else + + std::vector expanded_args_temp; + for (size_t source_idx = 0; source_idx < nArgc; source_idx++) { - _tcscpy_s(szParentDir, MAX_PATH, pszPathArg); + if (source_idx == pivot_parameter_idx) + { + ExpandArgument(ppszArgv[source_idx], expanded_args_temp); + } + else + { + expanded_args_temp.push_back(allocate_and_copy(ppszArgv[source_idx])); + } } - _tcscpy_s(ppszExpandedArgv[nPathArgIndex], MAX_PATH, _T("--appbase")); - _tcscpy_s(ppszExpandedArgv[nPathArgIndex + 1], MAX_PATH, szParentDir); - _tcscpy_s(ppszExpandedArgv[nPathArgIndex + 2], MAX_PATH, _T("Microsoft.Framework.ApplicationHost")); + nExpandedArgc = expanded_args_temp.size(); + ppszExpandedArgv = new dnx::char_t*[nExpandedArgc]; - for (int i = nPathArgIndex + 1; i < nArgc; ++i) + for (size_t i = 0; i < nExpandedArgc; i++) { - // Copy all other arguments/options as is - _tcscpy_s(ppszExpandedArgv[i + 2], MAX_PATH, ppszArgv[i]); + ppszExpandedArgv[i] = const_cast(expanded_args_temp[i]); } return true; } -bool GetApplicationBase(const dnx::xstring_t& currentDirectory, int argc, dnx::char_t* argv[], /*out*/ dnx::char_t* fullAppBasePath) +void FreeExpandedCommandLineArguments(size_t nArgc, dnx::char_t** ppszArgv) +{ + for (size_t i = 0; i < nArgc; ++i) + { + delete[] ppszArgv[i]; + } + delete[] ppszArgv; +} + +bool GetApplicationBase(const dnx::xstring_t& currentDirectory, size_t argc, dnx::char_t* argv[], /*out*/ dnx::char_t* fullAppBasePath) { dnx::char_t buffer[MAX_PATH]; const dnx::char_t* appBase = GetAppBaseParameterValue(argc, argv); @@ -244,7 +233,7 @@ bool GetApplicationBase(const dnx::xstring_t& currentDirectory, int argc, dnx::c return GetFullPath(appBase, fullAppBasePath) != 0; } -int CallApplicationProcessMain(int argc, dnx::char_t* argv[], dnx::trace_writer& trace_writer) +int CallApplicationProcessMain(size_t argc, dnx::char_t* argv[], dnx::trace_writer& trace_writer) { // Set the DNX_CONOSLE_HOST flag which will print exceptions to stderr instead of throwing SetConsoleHost(); @@ -252,13 +241,14 @@ int CallApplicationProcessMain(int argc, dnx::char_t* argv[], dnx::trace_writer& auto currentDirectory = GetNativeBootstrapperDirectory(); // Set the DEFAULT_LIB environment variable to be the same directory as the exe - SetEnvironmentVariable(_T("DNX_DEFAULT_LIB"), currentDirectory.c_str()); + SetEnvironmentVariable(_X("DNX_DEFAULT_LIB"), currentDirectory.c_str()); CALL_APPLICATION_MAIN_DATA data = { 0 }; - data.argc = argc; + data.argc = static_cast(argc); data.argv = const_cast(argv); dnx::char_t appBaseBuffer[MAX_PATH]; + if (!GetApplicationBase(currentDirectory, argc, argv, appBaseBuffer)) { return 1; diff --git a/src/dnx/main.cpp b/src/dnx/main.cpp index c60e40ff0..ac420f877 100644 --- a/src/dnx/main.cpp +++ b/src/dnx/main.cpp @@ -5,10 +5,10 @@ #include "pal.h" #include "utils.h" - -int CallApplicationProcessMain(int argc, dnx::char_t* argv[], dnx::trace_writer& trace_writer); -void FreeExpandedCommandLineArguments(int argc, dnx::char_t** ppszArgv); -bool ExpandCommandLineArguments(int argc, dnx::char_t** ppszArgv, int& expanded_argc, dnx::char_t**& ppszExpandedArgv); +int CallApplicationProcessMain(size_t argc, dnx::char_t* argv[], dnx::trace_writer& trace_writer); +void FreeExpandedCommandLineArguments(size_t argc, dnx::char_t** ppszArgv); +bool ExpandCommandLineArguments(size_t argc, dnx::char_t** ppszArgv, size_t& expanded_argc, dnx::char_t**& ppszExpandedArgv); +bool strings_equal_ignore_case(const dnx::char_t* s1, const dnx::char_t* s2); #if defined(ARM) int wmain(int argc, wchar_t* argv[]) @@ -26,21 +26,21 @@ extern "C" int __stdcall DnxMain(int argc, wchar_t* argv[]) { break; } - if (dnx::utils::strings_equal_ignore_case(argv[i], _X("--appbase"))) + if (strings_equal_ignore_case(argv[i], _X("--appbase"))) { //skip path argument ++i; continue; } - if (dnx::utils::strings_equal_ignore_case(argv[i], _X("--debug"))) + if (strings_equal_ignore_case(argv[i], _X("--debug"))) { WaitForDebuggerToAttach(); break; } } - int nExpandedArgc = -1; - LPTSTR* ppszExpandedArgv = nullptr; + size_t nExpandedArgc = 0; + dnx::char_t** ppszExpandedArgv = nullptr; auto expanded = ExpandCommandLineArguments(argc - 1, &(argv[1]), nExpandedArgc, ppszExpandedArgv); auto trace_writer = dnx::trace_writer{ IsTracingEnabled() }; diff --git a/src/dnx/pal.h b/src/dnx/pal.h index 6fd0715e1..a0f5fe9df 100644 --- a/src/dnx/pal.h +++ b/src/dnx/pal.h @@ -11,10 +11,10 @@ dnx::xstring_t GetNativeBootstrapperDirectory(); void WaitForDebuggerToAttach(); bool IsTracingEnabled(); void SetConsoleHost(); -BOOL GetAppBasePathFromEnvironment(LPTSTR szPath); -BOOL GetFullPath(LPCTSTR szPath, LPTSTR szFullPath); +bool GetAppBasePathFromEnvironment(dnx::char_t* szPath); +bool GetFullPath(const dnx::char_t* szPath, dnx::char_t* szFullPath); int CallApplicationMain(const dnx::char_t* moduleName, const char* functionName, CALL_APPLICATION_MAIN_DATA* data, dnx::trace_writer& trace_writer); #ifndef SetEnvironmentVariable -BOOL SetEnvironmentVariable(LPCTSTR lpName, LPCTSTR lpValue); +bool SetEnvironmentVariable(const dnx::char_t* lpName, const dnx::char_t* lpValue); #endif //SetEnvironmentVariable diff --git a/src/dnx/pal.unix.cpp b/src/dnx/pal.unix.cpp index c92fed405..8b9ef0e84 100644 --- a/src/dnx/pal.unix.cpp +++ b/src/dnx/pal.unix.cpp @@ -28,28 +28,28 @@ void SetConsoleHost() } } -BOOL GetAppBasePathFromEnvironment(LPTSTR szPath) +bool GetAppBasePathFromEnvironment(char* szPath) { char* appBaseEnv = getenv("DNX_APPBASE"); if (appBaseEnv != NULL && strlen(appBaseEnv) < PATH_MAX) { strcpy(szPath, appBaseEnv); - return TRUE; + return true; } - return FALSE; + return false; } -BOOL GetFullPath(LPCTSTR szPath, LPTSTR szNormalizedPath) +bool GetFullPath(const char* szPath, char* szNormalizedPath) { if (realpath(szPath, szNormalizedPath) == nullptr) { printf("Failed to get full path of application base: %s\r\n", szPath); - return FALSE; + return false; } - return TRUE; + return true; } int CallApplicationMain(const char* moduleName, const char* functionName, CALL_APPLICATION_MAIN_DATA* data, dnx::trace_writer& trace_writer) @@ -92,7 +92,7 @@ int CallApplicationMain(const char* moduleName, const char* functionName, CALL_A } } -BOOL SetEnvironmentVariable(LPCTSTR lpName, LPCTSTR lpValue) +bool SetEnvironmentVariable(const char* lpName, const char* lpValue) { int ret; diff --git a/src/dnx/pal.win32.cpp b/src/dnx/pal.win32.cpp index a90c8ad8e..8823cdd23 100644 --- a/src/dnx/pal.win32.cpp +++ b/src/dnx/pal.win32.cpp @@ -52,27 +52,27 @@ void SetConsoleHost() } } -BOOL GetAppBasePathFromEnvironment(LPTSTR pszAppBase) +bool GetAppBasePathFromEnvironment(LPTSTR pszAppBase) { DWORD dwAppBase = GetEnvironmentVariable(_T("DNX_APPBASE"), pszAppBase, MAX_PATH); return dwAppBase != 0 && dwAppBase < MAX_PATH; } -BOOL GetFullPath(LPCTSTR szPath, LPTSTR pszNormalizedPath) +bool GetFullPath(LPCTSTR szPath, LPTSTR pszNormalizedPath) { DWORD dwFullAppBase = GetFullPathName(szPath, MAX_PATH, pszNormalizedPath, nullptr); if (!dwFullAppBase) { ::_tprintf_s(_T("Failed to get full path of application base: %s\r\n"), szPath); - return FALSE; + return false; } else if (dwFullAppBase > MAX_PATH) { ::_tprintf_s(_T("Full path of application base is too long\r\n")); - return FALSE; + return false; } - return TRUE; + return true; } namespace diff --git a/src/dnx/stdafx.h b/src/dnx/stdafx.h index 621753c9b..14e8a7159 100644 --- a/src/dnx/stdafx.h +++ b/src/dnx/stdafx.h @@ -22,21 +22,16 @@ #include #include -typedef int BOOL; typedef uint32_t DWORD; typedef DWORD HRESULT; typedef void* HMODULE; typedef void* FARPROC; typedef void* HANDLE; -#include "tchar.h" - #define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) #define MAX_PATH PATH_MAX #define S_OK 0 -#define TRUE 1 -#define FALSE 0 inline int max(int a, int b) { return a > b ? a : b; } #endif // PLATFORM_UNIX diff --git a/src/dnx/tchar.cpp b/src/dnx/tchar.cpp deleted file mode 100644 index 91372acad..000000000 --- a/src/dnx/tchar.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -#include "stdafx.h" -#include "errno.h" - -int printf_s(LPCTSTR format, ...) -{ - va_list args; - va_start(args, format); - int ret = vprintf(format, args); - va_end(args); - - return ret; -} - -int _tcscpy_s(LPTSTR strDestination, size_t numberOfElements, LPCTSTR strSrc) -{ - if (strDestination == NULL || strSrc == NULL) - { - return EINVAL; - } - - if (numberOfElements == 0) - { - return ERANGE; - } - - strncpy(strDestination, strSrc, numberOfElements); - - - // strncpy will write null bytes to fill up strDestination if there - // was extra space. - if(strDestination[numberOfElements - 1] != '\0') - { - strDestination[0] = '\0'; - return ERANGE; - } - - return 0; -} diff --git a/src/dnx/tchar.h b/src/dnx/tchar.h deleted file mode 100644 index c75c1bf87..000000000 --- a/src/dnx/tchar.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -typedef char TCHAR; -typedef char* LPTSTR; -typedef const char* LPCTSTR; -typedef const char* LPCSTR; - -#define _T(x) x - -#define _tcsnicmp strncasecmp -#define _tcsnlen strnlen -#define _tprintf_s printf_s - -int printf_s(LPCTSTR format, ...); -int _tcscpy_s(LPTSTR strDestination, size_t numberOfElements, LPCTSTR strSrc); diff --git a/test/Bootstrapper.FunctionalTests/BootstrapperTests.cs b/test/Bootstrapper.FunctionalTests/BootstrapperTests.cs index b1549ca95..5492e390b 100644 --- a/test/Bootstrapper.FunctionalTests/BootstrapperTests.cs +++ b/test/Bootstrapper.FunctionalTests/BootstrapperTests.cs @@ -114,7 +114,7 @@ public void BootstrapperShowsVersionAndReturnsZeroExitCodeWhenVersionOptionWasGi [Theory] [MemberData(nameof(RuntimeComponents))] - public void BootstrapperInvokesApplicationHostWithInferredAppBase_ProjectDirAsArgument(string flavor, string os, string architecture) + public void BootstrapperInvokesApplicationHostWithExplicitAppBase(string flavor, string os, string architecture) { var runtimeHomeDir = _fixture.GetRuntimeHomeDir(flavor, os, architecture); @@ -125,7 +125,7 @@ public void BootstrapperInvokesApplicationHostWithInferredAppBase_ProjectDirAsAr string stdOut, stdErr; var exitCode = BootstrapperTestUtils.ExecBootstrapper( runtimeHomeDir, - arguments: string.Format("{0} run", testAppPath), + arguments: $"--appbase {testAppPath} Microsoft.Framework.ApplicationHost run", stdOut: out stdOut, stdErr: out stdErr, environment: new Dictionary { { EnvironmentNames.Trace, null } }); @@ -143,6 +143,39 @@ public void BootstrapperInvokesApplicationHostWithInferredAppBase_ProjectDirAsAr } } + [Theory] + [MemberData(nameof(RuntimeComponents))] + public void BootstrapperInvokesApplicationHostWithNoAppbase(string flavor, string os, string architecture) + { + var runtimeHomeDir = _fixture.GetRuntimeHomeDir(flavor, os, architecture); + + using (var tempSamplesDir = TestUtils.PrepareTemporarySamplesFolder(runtimeHomeDir)) + { + var testAppPath = Path.Combine(tempSamplesDir, "HelloWorld"); + + string stdOut, stdErr; + var exitCode = BootstrapperTestUtils.ExecBootstrapper( + runtimeHomeDir, + arguments: "run", + stdOut: out stdOut, + stdErr: out stdErr, + environment: new Dictionary { { EnvironmentNames.Trace, null } }, + workingDir: testAppPath); + + Assert.Equal(0, exitCode); + Assert.Equal(@"Hello World! +Hello, code! +I +can +customize +the +default +command +", stdOut); + } + } + + [Theory] [MemberData(nameof(RuntimeComponents))] public void BootstrapperInvokesApplicationHostWithInferredAppBase_ProjectFileAsArgument(string flavor, string os, string architecture) diff --git a/test/Microsoft.Framework.ApplicationHost.FunctionalTests/AppHostTests.cs b/test/Microsoft.Framework.ApplicationHost.FunctionalTests/AppHostTests.cs index 4cbf633d5..cf58aff38 100644 --- a/test/Microsoft.Framework.ApplicationHost.FunctionalTests/AppHostTests.cs +++ b/test/Microsoft.Framework.ApplicationHost.FunctionalTests/AppHostTests.cs @@ -92,9 +92,10 @@ public void AppHostShowsErrorWhenNoProjectJsonWasFound(string flavor, string os, string stdOut, stdErr; var exitCode = BootstrapperTestUtils.ExecBootstrapper( runtimeHomeDir, - arguments: $"{emptyFolder} run", + arguments: "run", stdOut: out stdOut, - stdErr: out stdErr); + stdErr: out stdErr, + workingDir: emptyFolder); Assert.NotEqual(0, exitCode); Assert.Contains("Unable to resolve project", stdErr); @@ -117,10 +118,11 @@ public void AppHostShowsErrorWhenGivenSubcommandWasNotFoundInProjectJson(string string stdOut, stdErr; var exitCode = BootstrapperTestUtils.ExecBootstrapper( runtimeHomeDir, - arguments: $"{projectPath} invalid", + arguments: "invalid", stdOut: out stdOut, stdErr: out stdErr, - environment: new Dictionary { { EnvironmentNames.AppBase, projectPath } }); + environment: new Dictionary { { EnvironmentNames.AppBase, projectPath } }, + workingDir: projectPath); Assert.NotEqual(0, exitCode); Assert.Contains("Unable to load application or execute command 'invalid'.", stdErr); @@ -153,9 +155,10 @@ public void AppHostShowsErrorWhenCurrentTargetFrameworkWasNotFoundInProjectJson( string stdOut, stdErr; var exitCode = BootstrapperTestUtils.ExecBootstrapper( runtimeHomeDir, - arguments: $"{projectPath} run", + arguments: $"run", stdOut: out stdOut, - stdErr: out stdErr); + stdErr: out stdErr, + workingDir: projectPath); var expectedErrorMsg =$@"The current runtime target framework is not compatible with '{projectName}'. @@ -291,10 +294,11 @@ public void Main(string[] arguments) string error; exitCode = BootstrapperTestUtils.ExecBootstrapper( runtimeHomePath, - arguments: $@"""{ projectPath }"" { command } extra", + arguments: $@" { command } extra", stdOut: out output, stdErr: out error, - environment: environment); + environment: environment, + workingDir: projectPath); Assert.Equal(0, exitCode); Assert.Empty(error); diff --git a/test/dnx.tests/pal.tests.cpp b/test/dnx.tests/pal.tests.cpp index cf9a91cc8..2bc7c0429 100644 --- a/test/dnx.tests/pal.tests.cpp +++ b/test/dnx.tests/pal.tests.cpp @@ -4,7 +4,7 @@ #pragma once #include "stdafx.h" -#include "xplat.h" +#include #include "trace_writer.h" #include "app_main.h" @@ -12,9 +12,9 @@ dnx::xstring_t GetNativeBootstrapperDirectory() { return L""; } void WaitForDebuggerToAttach() {} bool IsTracingEnabled() { return true; } void SetConsoleHost() {} -BOOL GetAppBasePathFromEnvironment(LPTSTR /*szPath*/) { return false; }; -BOOL GetFullPath(LPCTSTR /*szPath*/, LPTSTR /*szFullPath*/) { return false; } -int CallApplicationMain(const dnx::char_t* /*moduleName*/, const char* /*functionName*/, CALL_APPLICATION_MAIN_DATA* /*data*/, dnx::trace_writer& /*trace_writer*/) { return 3; } +bool GetAppBasePathFromEnvironment(wchar_t* /*szPath*/) { return false; }; +bool GetFullPath(const wchar_t* /*szPath*/, wchar_t* /*szFullPath*/) { return false; } +int CallApplicationMain(const wchar_t* /*moduleName*/, const char* /*functionName*/, CALL_APPLICATION_MAIN_DATA* /*data*/, dnx::trace_writer& /*trace_writer*/) { return 3; } #ifndef SetEnvironmentVariable BOOL SetEnvironmentVariable(LPCTSTR lpName, LPCTSTR lpValue); diff --git a/test/dnx.tests/parameter_expansion_tests.cpp b/test/dnx.tests/parameter_expansion_tests.cpp index 0d5e5b8b3..c1c4c5c5a 100644 --- a/test/dnx.tests/parameter_expansion_tests.cpp +++ b/test/dnx.tests/parameter_expansion_tests.cpp @@ -3,13 +3,156 @@ #include "stdafx.h" #include "xplat.h" +#include +#include -bool ExpandCommandLineArguments(int nArgc, dnx::char_t** ppszArgv, int& nExpandedArgc, dnx::char_t**& ppszExpandedArgv); +bool ExpandCommandLineArguments(size_t nArgc, dnx::char_t** ppszArgv, size_t& nExpandedArgc, dnx::char_t**& ppszExpandedArgv); +void FreeExpandedCommandLineArguments(size_t nArgc, dnx::char_t** ppszArgv); -TEST(parameter_expansion, ExpandCommandLineArguments_returns_false_when_no_params) +template +void test_ExpandCommandLineArguments(dnx::char_t*(&args)[arg_count], bool should_expand, std::vector& expected_expanded_args) { - int expanded_arg_count; - dnx::char_t** expanded_argv = nullptr; - ASSERT_FALSE(ExpandCommandLineArguments(0, nullptr, expanded_arg_count, expanded_argv)); - ASSERT_EQ(nullptr, expanded_argv); + size_t expanded_arg_count; + dnx::char_t** expanded_args = nullptr; + + ASSERT_EQ(should_expand, ExpandCommandLineArguments(arg_count, args, expanded_arg_count, expanded_args)); + + if (should_expand) + { + ASSERT_EQ(expected_expanded_args.size(), expanded_arg_count); + for (auto i = 0u; i < expanded_arg_count; i++) + { + ASSERT_STREQ(expected_expanded_args[i], expanded_args[i]); + } + + FreeExpandedCommandLineArguments(expanded_arg_count, expanded_args); + } +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_not_expand_when_no_params) +{ + size_t expanded_arg_count; + dnx::char_t** expanded_args = nullptr; + ASSERT_FALSE(ExpandCommandLineArguments(0u, nullptr, expanded_arg_count, expanded_args)); + ASSERT_EQ(nullptr, expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_expand_dot) +{ + dnx::char_t* args[] + { _X("."), _X("run") }; + std::vector expected_expanded_args( + { _X("--appbase"), _X("."), _X("Microsoft.Framework.ApplicationHost"), _X("run") }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_ignore_appbase_after_bootstrapper_commands) +{ + dnx::char_t* args[] + { _X("."), _X("run"), _X("--appbase"), _X("C:\\temp") }; + std::vector expected_expanded_args( + { _X("--appbase"), _X("."), _X("Microsoft.Framework.ApplicationHost"), _X("run"), _X("--appbase"), _X("C:\\temp") }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_not_expand_params_if_appbase_after_parameter_with_argument) +{ + dnx::char_t* args[] { _X("--port"), _X("1234"), _X("--appbase"), _X("C:\\temp"), _X("run") }; + size_t expanded_arg_count; + dnx::char_t** expanded_args = nullptr; + + ASSERT_FALSE(ExpandCommandLineArguments(0u, args, expanded_arg_count, expanded_args)); + ASSERT_EQ(nullptr, expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_expand_params_and_add_implicit_appbase_path_for_dll) +{ + dnx::char_t* args[] + { _X("--port"), _X("1234"), _X("MyApp.dll"), _X("param")}; + std::vector expected_expanded_args( + { _X("--port"), _X("1234"), _X("--appbase"), _X("."), _X("MyApp.dll"), _X("param") }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_expand_params_and_add_implicit_appbase_path_for_exe) +{ + dnx::char_t* args[] + { _X("--port"), _X("1234"), _X("MyApp.exe") }; + std::vector expected_expanded_args( + { _X("--port"), _X("1234"), _X("--appbase"), _X("."), _X("MyApp.exe") }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_expand_params_and_copy_appbase_path_for_dll_with_path) +{ + dnx::char_t* args[] + { _X("--port"), _X("1234"), _X("C:\\app\\MyApp.dll") }; + std::vector expected_expanded_args( + { _X("--port"), _X("1234"), _X("--appbase"), _X("C:\\app\\"), _X("C:\\app\\MyApp.dll") }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_expand_params_and_copy_appbase_path_for_exe_with_path) +{ + dnx::char_t* args[] + { _X("--port"), _X("1234"), _X("/MyApp.exe") }; + std::vector expected_expanded_args( + { _X("--port"), _X("1234"), _X("--appbase"), _X("/"), _X("/MyApp.exe") }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_expand_params_and_add_implicit_appbase_for_project_json) +{ + dnx::char_t* args[] + { _X("project.json"), _X("run") }; + std::vector expected_expanded_args( + { _X("--appbase"), _X("."), _X("Microsoft.Framework.ApplicationHost"), _X("run") }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_expand_params_and_add_appbase_for_project_json_path) +{ + dnx::char_t* args[] + { _X("C:\\MyApp\\project.json"), _X("run") }; + std::vector expected_expanded_args( + { _X("--appbase"), _X("C:\\MyApp\\"), _X("Microsoft.Framework.ApplicationHost"), _X("run"), }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_expand_implicit_appbase_to_dot) +{ + dnx::char_t* args[] + { _X("run") }; + std::vector expected_expanded_args( + { _X("--appbase"), _X("."), _X("Microsoft.Framework.ApplicationHost"), _X("run") }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_expand_implicit_appbase_to_dot_for_command_after_bootstrapper_params) +{ + dnx::char_t* args[] + { _X("--port"), _X("1234"), _X("run") }; + std::vector expected_expanded_args( + { _X("--port"), _X("1234"), _X("--appbase"), _X("."), _X("Microsoft.Framework.ApplicationHost"), _X("run") }); + + test_ExpandCommandLineArguments(args, true, expected_expanded_args); +} + +TEST(parameter_expansion, ExpandCommandLineArguments_should_not_expand_if_appbase_parameter_value_missing) +{ + dnx::char_t* args[] + { _X("--port"), _X("1234"), _X("--appbase") }; + size_t expanded_arg_count; + dnx::char_t** expanded_args = nullptr; + ASSERT_FALSE(ExpandCommandLineArguments(3u, args, expanded_arg_count, expanded_args)); + ASSERT_EQ(nullptr, expanded_args); } \ No newline at end of file