Skip to content

Commit f3c705e

Browse files
Implement support for passing a .env file to corerun to easily set environment variables for stress testing. (#58222)
* Implement support for passing a .env file to corerun to easily set environment variables for stress testing. * Load the parsed dotenv into the process. * Implement support for # comments * Clean up code * Avoid copy * PR feedback * Update docs and bash/batch scripts to support the dotenv files. * Use what clang recommends to not copy * Apply suggestions from code review Co-authored-by: Aaron Robinson <arobins@microsoft.com> Co-authored-by: Aaron Robinson <arobins@microsoft.com>
1 parent 4173838 commit f3c705e

File tree

8 files changed

+687
-47
lines changed

8 files changed

+687
-47
lines changed

docs/workflow/testing/using-corerun.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ variable `CORE_ROOT` to this directory &ndash; you don't have to set `CORE_LIBRA
7575
set PATH=%PATH%;<repo_root>\artifacts\Product\windows.x64.Debug
7676
set CORE_ROOT=<repo_root>\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root
7777
```
78-
For example, the following runs the finalizerio test on Windows.
78+
For example, the following runs the finalizeio test on Windows.
7979

8080
```cmd
8181
corerun artifacts\tests\coreclr\windows.x64.Debug\GC\Features\Finalizer\finalizeio\finalizeio\finalizeio.dll
@@ -98,3 +98,6 @@ See `corerun --help` for additional details.
9898

9999
`--debug` - Wait for a debugger to attach prior to loading the .NET runtime.
100100
- For example, `corerun --debug HelloWorld.dll`
101+
102+
`--env` - Pass the path to a file in the [`dotenv`](https://github.com/motdotla/dotenv) format to specify environment variables for the test run.
103+
- For example, `corerun --env gcstress.env HelloWorld.dll`.

src/coreclr/hosts/corerun/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ endif(CLR_CMAKE_HOST_WIN32)
1111

1212
add_executable_clr(corerun
1313
corerun.cpp
14+
dotenv.cpp
1415
native.rc
1516
)
1617

src/coreclr/hosts/corerun/corerun.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
#include <coreclrhost.h>
66

77
#include "corerun.hpp"
8+
#include "dotenv.hpp"
9+
10+
#include <fstream>
811

912
using char_t = pal::char_t;
1013
using string_t = pal::string_t;
@@ -50,6 +53,9 @@ struct configuration
5053

5154
// Perform self test.
5255
bool self_test;
56+
57+
// configured .env file to load
58+
dotenv dotenv_configuration;
5359
};
5460

5561
namespace envvar
@@ -262,6 +268,8 @@ static int run(const configuration& config)
262268
}
263269
}
264270

271+
config.dotenv_configuration.load_into_current_process();
272+
265273
actions.before_coreclr_load();
266274

267275
// Attempt to load CoreCLR.
@@ -404,6 +412,7 @@ static void display_usage()
404412
W(" If a property value contains spaces, quote the entire argument.\n")
405413
W(" May be supplied multiple times. Format: <key>=<value>.\n")
406414
W(" -d, --debug - causes corerun to wait for a debugger to attach before executing.\n")
415+
W(" -e, --env - path to a .env file with environment variables that corerun should set.\n")
407416
W(" -?, -h, --help - show this help.\n")
408417
W("\n")
409418
W("The runtime binary is searched for in --clr-path, CORE_ROOT environment variable, then\n")
@@ -508,6 +517,18 @@ static bool parse_args(
508517
config.self_test = true;
509518
return true;
510519
}
520+
else if (pal::strcmp(option, W("e")) == 0 || (pal::strcmp(option, W("env")) == 0))
521+
{
522+
i++;
523+
if (i >= argc)
524+
{
525+
pal::fprintf(stderr, W("Option %s: missing .env file path\n"), arg);
526+
break;
527+
}
528+
529+
std::ifstream dotenvFile{ pal::convert_to_utf8(argv[i]) };
530+
config.dotenv_configuration = dotenv{ pal::string_t{ argv[i] }, dotenvFile};
531+
}
511532
else if ((pal::strcmp(option, W("?")) == 0 || (pal::strcmp(option, W("h")) == 0 || (pal::strcmp(option, W("help")) == 0))))
512533
{
513534
display_usage();
@@ -640,6 +661,9 @@ static int self_test()
640661
THROW_IF_FALSE(!pal::string_ends_with(W("ab.cd"), W(".cde")));
641662
THROW_IF_FALSE(!pal::string_ends_with(W("ab.cd"), W("ab.cde")));
642663
}
664+
{
665+
dotenv::self_test();
666+
}
643667
}
644668
catch (const char_t msg[])
645669
{

src/coreclr/hosts/corerun/corerun.hpp

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,19 @@ namespace pal
6363
const char_t nativelib_ext[] = W(".dll");
6464
const char_t coreclr_lib[] = W("coreclr");
6565

66-
int strcmp(const char_t* str1, const char_t* str2) { return wcscmp(str1, str2); }
67-
size_t strlen(const char_t* str) { return wcslen(str); }
68-
char_t* strdup(const char_t* str) { return ::_wcsdup(str); }
69-
int fprintf(FILE* fd, const char_t* const fmt, ...)
66+
inline int strcmp(const char_t* str1, const char_t* str2) { return wcscmp(str1, str2); }
67+
inline size_t strlen(const char_t* str) { return wcslen(str); }
68+
inline char_t* strdup(const char_t* str) { return ::_wcsdup(str); }
69+
inline int fprintf(FILE* fd, const char_t* const fmt, ...)
7070
{
7171
va_list args;
7272
va_start(args, fmt);
7373
int ret = ::vfwprintf(fd, fmt, args);
7474
va_end(args);
7575
return ret;
7676
}
77-
bool is_cli_option(const char_t option_maybe) { return option_maybe == W('-') || option_maybe == W('/'); }
78-
string_t getenv(const char_t* var)
77+
inline bool is_cli_option(const char_t option_maybe) { return option_maybe == W('-') || option_maybe == W('/'); }
78+
inline string_t getenv(const char_t* var)
7979
{
8080
DWORD needed = ::GetEnvironmentVariableW(var, nullptr, 0);
8181
if (needed == 0)
@@ -87,15 +87,20 @@ namespace pal
8787
assert(wrote < needed);
8888
return { buffer.get() };
8989
}
90-
string_t get_exe_path()
90+
inline void setenv(const char_t* var, string_t value)
91+
{
92+
BOOL success = ::SetEnvironmentVariableW(var, value.c_str());
93+
assert(success);
94+
}
95+
inline string_t get_exe_path()
9196
{
9297
char_t file_name[1024];
9398
DWORD count = ::GetModuleFileNameW(nullptr, file_name, ARRAYSIZE(file_name));
9499
assert(::GetLastError() != ERROR_INSUFFICIENT_BUFFER);
95100

96101
return { file_name };
97102
}
98-
string_t get_absolute_path(const char_t* path)
103+
inline string_t get_absolute_path(const char_t* path)
99104
{
100105
DWORD needed = ::GetFullPathNameW(path, 0, nullptr, nullptr);
101106
malloc_ptr<char_t> buffer{ (char_t*)::malloc(needed * sizeof(char_t)) };
@@ -106,25 +111,25 @@ namespace pal
106111
return { buffer.get() };
107112
}
108113

109-
uint32_t get_process_id()
114+
inline uint32_t get_process_id()
110115
{
111116
return (uint32_t)::GetCurrentProcessId();
112117
}
113118

114-
debugger_state_t is_debugger_attached()
119+
inline debugger_state_t is_debugger_attached()
115120
{
116121
return (::IsDebuggerPresent() == TRUE) ? debugger_state_t::attached : debugger_state_t::not_attached;
117122
}
118123

119-
bool does_file_exist(const string_t& file_path)
124+
inline bool does_file_exist(const string_t& file_path)
120125
{
121126
return INVALID_FILE_ATTRIBUTES != ::GetFileAttributesW(file_path.c_str());
122127
}
123128

124129
// Forward declaration
125130
void ensure_trailing_delimiter(pal::string_t& dir);
126131

127-
string_t build_file_list(
132+
inline string_t build_file_list(
128133
const string_t& dir,
129134
const char_t* ext,
130135
std::function<bool(const char_t*)> should_add)
@@ -163,13 +168,13 @@ namespace pal
163168
return file_list.str();
164169
}
165170

166-
void* get_module_symbol(mod_t m, const char* sym)
171+
inline void* get_module_symbol(mod_t m, const char* sym)
167172
{
168173
assert(m != nullptr && sym != nullptr);
169174
return ::GetProcAddress((HMODULE)m, sym);
170175
}
171176

172-
string_utf8_t convert_to_utf8(const char_t* str)
177+
inline string_utf8_t convert_to_utf8(const char_t* str)
173178
{
174179
// Compute the needed buffer
175180
int bytes_req = ::WideCharToMultiByte(
@@ -191,12 +196,12 @@ namespace pal
191196
return { buffer.get() };
192197
}
193198

194-
string_utf8_t convert_to_utf8(string_t&& str)
199+
inline string_utf8_t convert_to_utf8(string_t&& str)
195200
{
196201
return convert_to_utf8(str.c_str());
197202
}
198203

199-
bool try_load_hostpolicy(pal::string_t mock_hostpolicy_value)
204+
inline bool try_load_hostpolicy(pal::string_t mock_hostpolicy_value)
200205
{
201206
const char_t* hostpolicyName = W("hostpolicy.dll");
202207
pal::mod_t hMod = (pal::mod_t)::GetModuleHandleW(hostpolicyName);
@@ -213,7 +218,7 @@ namespace pal
213218
return hMod != nullptr;
214219
}
215220

216-
bool try_load_coreclr(const pal::string_t& core_root, pal::mod_t& hMod)
221+
inline bool try_load_coreclr(const pal::string_t& core_root, pal::mod_t& hMod)
217222
{
218223
pal::string_t coreclr_path = core_root;
219224
pal::ensure_trailing_delimiter(coreclr_path);
@@ -335,29 +340,34 @@ namespace pal
335340
#endif
336341
const char_t coreclr_lib[] = W("libcoreclr");
337342

338-
int strcmp(const char_t* str1, const char_t* str2) { return ::strcmp(str1, str2); }
339-
size_t strlen(const char_t* str) { return ::strlen(str); }
340-
char_t* strdup(const char_t* str) { return ::strdup(str); }
341-
int fprintf(FILE* fd, const char_t* const fmt, ...)
343+
inline int strcmp(const char_t* str1, const char_t* str2) { return ::strcmp(str1, str2); }
344+
inline size_t strlen(const char_t* str) { return ::strlen(str); }
345+
inline char_t* strdup(const char_t* str) { return ::strdup(str); }
346+
inline int fprintf(FILE* fd, const char_t* const fmt, ...)
342347
{
343348
va_list args;
344349
va_start(args, fmt);
345350
int ret = ::vfprintf(fd, fmt, args);
346351
va_end(args);
347352
return ret;
348353
}
349-
bool is_cli_option(const char_t option_maybe) { return option_maybe == W('-'); }
350-
string_t getenv(const char_t* var)
354+
inline bool is_cli_option(const char_t option_maybe) { return option_maybe == W('-'); }
355+
inline string_t getenv(const char_t* var)
351356
{
352357
const char_t* val = ::getenv(var);
353358
if (val == nullptr)
354359
return {};
355360
return { val };
356361
}
362+
inline void setenv(const char_t* var, string_t value)
363+
{
364+
int error = ::setenv(var, value.c_str(), /* overwrite */ 1);
365+
assert(error == 0);
366+
}
357367

358-
string_t get_exe_path() { return { getexepath() }; }
368+
inline string_t get_exe_path() { return { getexepath() }; }
359369

360-
string_t get_absolute_path(const string_t& path)
370+
inline string_t get_absolute_path(const string_t& path)
361371
{
362372
string_t abs_path = path;
363373

@@ -372,12 +382,12 @@ namespace pal
372382
return abs_path;
373383
}
374384

375-
uint32_t get_process_id()
385+
inline uint32_t get_process_id()
376386
{
377387
return (uint32_t)getpid();
378388
}
379389

380-
debugger_state_t is_debugger_attached()
390+
inline debugger_state_t is_debugger_attached()
381391
{
382392
#if defined(__APPLE__)
383393
// Taken from https://developer.apple.com/library/archive/qa/qa1361/_index.html
@@ -452,7 +462,7 @@ namespace pal
452462
#endif // !__APPLE__
453463
}
454464

455-
bool does_file_exist(const char_t* file_path)
465+
inline bool does_file_exist(const char_t* file_path)
456466
{
457467
// Check if the specified path exists
458468
struct stat sb;
@@ -478,7 +488,7 @@ namespace pal
478488
bool string_ends_with(const string_t& str, size_t suffix_len, const char_t* suffix);
479489
void ensure_trailing_delimiter(pal::string_t& dir);
480490

481-
string_t build_file_list(
491+
inline string_t build_file_list(
482492
const string_t& directory,
483493
const char_t* ext,
484494
std::function<bool(const char_t*)> should_add)
@@ -539,23 +549,23 @@ namespace pal
539549
return file_list.str();
540550
}
541551

542-
void* get_module_symbol(mod_t m, const char* sym)
552+
inline void* get_module_symbol(mod_t m, const char* sym)
543553
{
544554
assert(m != nullptr && sym != nullptr);
545555
return dlsym(m, sym);
546556
}
547557

548-
string_utf8_t convert_to_utf8(const char_t* str)
558+
inline string_utf8_t convert_to_utf8(const char_t* str)
549559
{
550560
return { str };
551561
}
552562

553-
string_utf8_t convert_to_utf8(string_t&& str)
563+
inline string_utf8_t convert_to_utf8(string_t&& str)
554564
{
555565
return std::move(str);
556566
}
557567

558-
bool try_load_hostpolicy(pal::string_t mock_hostpolicy_value)
568+
inline bool try_load_hostpolicy(pal::string_t mock_hostpolicy_value)
559569
{
560570
if (!string_ends_with(mock_hostpolicy_value, pal::nativelib_ext))
561571
mock_hostpolicy_value.append(pal::nativelib_ext);
@@ -567,13 +577,13 @@ namespace pal
567577
return hMod != nullptr;
568578
}
569579

570-
bool try_load_coreclr(const pal::string_t& core_root, pal::mod_t& hMod)
580+
inline bool try_load_coreclr(const pal::string_t& core_root, pal::mod_t& hMod)
571581
{
572582
pal::string_t coreclr_path = core_root;
573583
pal::ensure_trailing_delimiter(coreclr_path);
574584
coreclr_path.append(pal::coreclr_lib);
575585
coreclr_path.append(pal::nativelib_ext);
576-
586+
577587
hMod = (pal::mod_t)dlopen(coreclr_path.c_str(), RTLD_NOW | RTLD_LOCAL);
578588
if (hMod == nullptr)
579589
{
@@ -616,7 +626,7 @@ class platform_specific_actions final
616626

617627
namespace pal
618628
{
619-
void split_path_to_dir_filename(const pal::string_t& path, pal::string_t& dir, pal::string_t& filename)
629+
inline void split_path_to_dir_filename(const pal::string_t& path, pal::string_t& dir, pal::string_t& filename)
620630
{
621631
size_t pos = path.find_last_of(dir_delim);
622632
if (pos == pal::string_t::npos)
@@ -630,7 +640,7 @@ namespace pal
630640
filename = path.substr(pos + 1);
631641
}
632642

633-
bool string_ends_with(const string_t& str, size_t suffix_len, const char_t* suffix)
643+
inline bool string_ends_with(const string_t& str, size_t suffix_len, const char_t* suffix)
634644
{
635645
assert(suffix != nullptr);
636646

@@ -648,7 +658,7 @@ namespace pal
648658
return string_ends_with(str, LEN - 1, suffix);
649659
}
650660

651-
void ensure_trailing_delimiter(pal::string_t& dir)
661+
inline void ensure_trailing_delimiter(pal::string_t& dir)
652662
{
653663
if (dir.empty())
654664
{
@@ -660,7 +670,7 @@ namespace pal
660670
}
661671
}
662672

663-
const char** convert_argv_to_utf8(int argc, const char_t** argv, std::vector<string_utf8_t>& lifetime)
673+
inline const char** convert_argv_to_utf8(int argc, const char_t** argv, std::vector<string_utf8_t>& lifetime)
664674
{
665675
malloc_ptr<const char*> ret{ (const char**)::malloc(sizeof(char*) * argc) };
666676
assert(ret != nullptr);

0 commit comments

Comments
 (0)