{
pal::string_t probe_dir;
bool patch_roll_fwd;
- bool prerelease_roll_fwd;
const deps_json_t* probe_deps_json;
int fx_level;
void fx_definition_t::parse_runtime_config(
const pal::string_t& path,
const pal::string_t& dev_path,
- const runtime_config_t* defaults
+ const runtime_config_t* higher_layer_config,
+ const runtime_config_t* app_config
)
{
- m_runtime_config.parse(path, dev_path, defaults);
+ m_runtime_config.parse(path, dev_path, higher_layer_config, app_config);
}
void fx_definition_t::parse_deps()
const pal::string_t& get_found_version() const { return m_found_version; }
const pal::string_t& get_dir() const { return m_dir; }
const runtime_config_t& get_runtime_config() const { return m_runtime_config; }
- void parse_runtime_config(const pal::string_t& path, const pal::string_t& dev_path, const runtime_config_t* defaults);
+ void parse_runtime_config(const pal::string_t& path, const pal::string_t& dev_path, const runtime_config_t* higher_layer_config, const runtime_config_t* app_config);
const pal::string_t& get_deps_file() const { return m_deps_file; }
void set_deps_file(const pal::string_t value) { m_deps_file = value; }
get_runtime_config_paths_from_arg(runtime_config, &config_file, &dev_config_file);
}
- app.parse_runtime_config(config_file, dev_config_file, nullptr);
+ app.parse_runtime_config(config_file, dev_config_file, nullptr, nullptr);
if (!app.get_runtime_config().is_valid())
{
trace::error(_X("Invalid runtimeconfig.json [%s] [%s]"), app.get_runtime_config().get_path().c_str(), app.get_runtime_config().get_dev_path().c_str());
return rc;
}
- auto config = app->get_runtime_config();
+ auto app_config = app->get_runtime_config();
// 'Roll forward on no candidate fx' is set to 1 (roll_fwd_on_no_candidate_fx_option::minor) by default. It can be changed through:
- // 1. Command line argument (--roll-forward-on-no-candidate-fx). Only defaults the app's config.
- // 2. Runtimeconfig json file ('rollForwardOnNoCandidateFx' property), which is used as a default for lower level frameworks if they don't specify a value.
- // 3. DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX env var. Only defaults the app's config.
- // The conflicts will be resolved by following the priority rank described above (from 1 to 3).
+ // 1. Command line argument (--roll-forward-on-no-candidate-fx).
+ // 2. Runtimeconfig json file ('rollForwardOnNoCandidateFx' property in "framework" section:).
+ // 3. Runtimeconfig json file ('rollForwardOnNoCandidateFx' property), which is used as a default for lower level frameworks if they don't specify a value.
+ // 4. DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX env var. Only defaults the app's config.
+ // The conflicts will be resolved by following the priority rank described above (from 1 to 4).
// The env var condition is verified in the config file processing
if (!roll_fwd_on_no_candidate_fx.empty())
{
- config.set_roll_fwd_on_no_candidate_fx(static_cast<roll_fwd_on_no_candidate_fx_option>(pal::xtoi(roll_fwd_on_no_candidate_fx.c_str())));
+ app_config.force_roll_fwd_on_no_candidate_fx(static_cast<roll_fwd_on_no_candidate_fx_option>(pal::xtoi(roll_fwd_on_no_candidate_fx.c_str())));
}
+ auto config = app_config;
+
// Determine additional deps
pal::string_t additional_deps_serialized;
bool is_portable = config.get_portable();
pal::string_t config_file;
pal::string_t dev_config_file;
get_runtime_config_paths(fx->get_dir(), config.get_fx_name(), &config_file, &dev_config_file);
- fx->parse_runtime_config(config_file, dev_config_file, &config);
+ fx->parse_runtime_config(config_file, dev_config_file, &config, &app_config);
config = fx->get_runtime_config();
if (!config.is_valid())
#include "runtime_config.h"
#include <cassert>
+
+// The semantics of applying the runtimeconfig.json values follows, in the following steps from
+// first to last, where last always wins. These steps are also annotated in the code here.
+// 1a) If the app, apply the default values from the environment
+// 1b) If the framework, apply the default values from the higher framework
+// 2) Apply the values in the current "framework" section; use these as defaults for current layer
+// 3) Apply the values in the app's "additionalFrameworks" section for the targeted framework
+// 4) Apply the readonly values which are the settings that can't be changed by lower layers
+
runtime_config_t::runtime_config_t()
: m_patch_roll_fwd(true)
- , m_prerelease_roll_fwd(false)
, m_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option::minor)
, m_portable(false)
, m_valid(false)
{
}
-void runtime_config_t::parse(const pal::string_t& path, const pal::string_t& dev_path, const runtime_config_t* defaults)
+void runtime_config_t::parse(const pal::string_t& path, const pal::string_t& dev_path, const runtime_config_t* higher_layer_config, const runtime_config_t* app_config)
{
m_path = path;
m_dev_path = dev_path;
- // Apply the defaults from previous config.
- if (defaults)
+ // Step #1: apply the defaults from the environment (for the app) or previous\higher layer (for a framework)
+ if (higher_layer_config != nullptr)
{
- m_patch_roll_fwd = defaults->m_patch_roll_fwd;
- m_prerelease_roll_fwd = defaults->m_prerelease_roll_fwd;
- m_roll_fwd_on_no_candidate_fx = defaults->m_roll_fwd_on_no_candidate_fx;
+ // Copy the previous defaults so we can default the next framework; these may be changed by the current fx
+ copy_framework_settings_to(higher_layer_config->m_fx_global, m_fx_global);
+
+ // Apply the defaults
+ set_effective_values(m_fx_global);
}
else
{
if (pal::getenv(_X("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX"), &env_no_candidate))
{
m_roll_fwd_on_no_candidate_fx = static_cast<roll_fwd_on_no_candidate_fx_option>(pal::xtoi(env_no_candidate.c_str()));
+ m_fx_global.set_roll_fwd_on_no_candidate_fx(m_roll_fwd_on_no_candidate_fx);
}
}
- m_valid = ensure_parsed();
+ m_valid = ensure_parsed(app_config);
- // Overwrite values that can't be changed from previous config.
- if (defaults)
+ if (m_valid)
{
- m_tfm = defaults->m_tfm;
+ // Step #4: apply the readonly values
+ if (app_config != nullptr)
+ {
+ m_tfm = app_config->m_tfm;
+ set_effective_values(app_config->m_fx_readonly);
+ }
}
trace::verbose(_X("Runtime config [%s] is valid=[%d]"), path.c_str(), m_valid);
if (patch_roll_fwd != opts_obj.end())
{
m_patch_roll_fwd = patch_roll_fwd->second.as_bool();
- }
-
- auto prerelease_roll_fwd = opts_obj.find(_X("preReleaseRollForward"));
- if (prerelease_roll_fwd != opts_obj.end())
- {
- m_prerelease_roll_fwd = prerelease_roll_fwd->second.as_bool();
+ m_fx_global.set_patch_roll_fwd(m_patch_roll_fwd);
}
auto roll_fwd_on_no_candidate_fx = opts_obj.find(_X("rollForwardOnNoCandidateFx"));
if (roll_fwd_on_no_candidate_fx != opts_obj.end())
{
m_roll_fwd_on_no_candidate_fx = static_cast<roll_fwd_on_no_candidate_fx_option>(roll_fwd_on_no_candidate_fx->second.as_integer());
+ m_fx_global.set_roll_fwd_on_no_candidate_fx(m_roll_fwd_on_no_candidate_fx);
}
auto tfm = opts_obj.find(_X("tfm"));
m_tfm = tfm->second.as_string();
}
+ // Step #2: apply the "framework" section
+
auto framework = opts_obj.find(_X("framework"));
if (framework == opts_obj.end())
{
m_portable = true;
const auto& fx_obj = framework->second.as_object();
+
m_fx_name = fx_obj.at(_X("name")).as_string();
- m_fx_ver = fx_obj.at(_X("version")).as_string();
+
+ bool rc = parse_framework(fx_obj);
+ if (rc)
+ {
+ set_effective_values(m_fx);
+ }
+
+ return rc;
+}
+
+void runtime_config_t::set_effective_values(const runtime_config_framework_t& overrides)
+{
+ if (overrides.get_fx_ver() != nullptr)
+ {
+ m_fx_ver = *overrides.get_fx_ver();
+ }
+
+ if (overrides.get_roll_fwd_on_no_candidate_fx() != nullptr)
+ {
+ m_roll_fwd_on_no_candidate_fx = *overrides.get_roll_fwd_on_no_candidate_fx();
+ }
+
+ if (overrides.get_patch_roll_fwd() != nullptr)
+ {
+ m_patch_roll_fwd = *overrides.get_patch_roll_fwd();
+ }
+}
+
+/*static*/ void runtime_config_t::copy_framework_settings_to(const runtime_config_framework_t& from, runtime_config_framework_t& to)
+{
+ if (from.get_fx_ver() != nullptr)
+ {
+ to.set_fx_ver(*from.get_fx_ver());
+ }
+
+ if (from.get_roll_fwd_on_no_candidate_fx() != nullptr)
+ {
+ to.set_roll_fwd_on_no_candidate_fx(*from.get_roll_fwd_on_no_candidate_fx());
+ }
+
+ if (from.get_patch_roll_fwd() != nullptr)
+ {
+ to.set_patch_roll_fwd(*from.get_patch_roll_fwd());
+ }
+}
+
+bool runtime_config_t::parse_framework(const json_object& fx_obj)
+{
+ auto fx_ver = fx_obj.find(_X("version"));
+ if (fx_ver != fx_obj.end())
+ {
+ m_fx.set_fx_ver(fx_ver->second.as_string());
+ }
+
+ auto patch_roll_fwd = fx_obj.find(_X("applyPatches"));
+ if (patch_roll_fwd != fx_obj.end())
+ {
+ m_fx.set_patch_roll_fwd(patch_roll_fwd->second.as_bool());
+ }
+
+ auto roll_fwd_on_no_candidate_fx = fx_obj.find(_X("rollForwardOnNoCandidateFx"));
+ if (roll_fwd_on_no_candidate_fx != fx_obj.end())
+ {
+ m_fx.set_roll_fwd_on_no_candidate_fx(static_cast<roll_fwd_on_no_candidate_fx_option>(roll_fwd_on_no_candidate_fx->second.as_integer()));
+ }
return true;
}
return true;
}
-bool runtime_config_t::ensure_parsed()
+bool runtime_config_t::ensure_parsed(const runtime_config_t* app_config)
{
trace::verbose(_X("Attempting to read runtime config: %s"), m_path.c_str());
if (!ensure_dev_config_parsed())
trace::verbose(_X("UTF-8 BOM skipped while reading [%s]"), m_path.c_str());
}
+ bool rc = true;
try
{
const auto root = json_value::parse(file);
const auto iter = json.find(_X("runtimeOptions"));
if (iter != json.end())
{
- parse_opts(iter->second);
+ rc = parse_opts(iter->second);
+
+ if (rc)
+ {
+ if (app_config == nullptr)
+ {
+ // If there is no app_config yet, then we are the app
+ // Read the additionalFrameworks section so we can apply later when each framework's runtimeconfig is read
+ const auto& opts_obj = iter->second.as_object();
+ const auto iter = opts_obj.find(_X("additionalFrameworks"));
+ if (iter != opts_obj.end())
+ {
+ const auto& additional_frameworks = iter->second.as_array();
+ for (const auto& fx : additional_frameworks)
+ {
+ runtime_config_t fx_overrides;
+ const auto& fx_obj = fx.as_object();
+ fx_overrides.m_fx_name = fx_obj.at(_X("name")).as_string();
+ if (fx_overrides.m_fx_name.length() == 0)
+ {
+ trace::verbose(_X("No framework name in additionalFrameworks section."));
+ rc = false;
+ break;
+ }
+
+ rc = fx_overrides.parse_framework(fx_obj);
+ if (!rc)
+ {
+ break;
+ }
+
+ m_additional_frameworks[fx_overrides.m_fx_name] = fx_overrides.m_fx;
+ }
+ }
+
+ // Follow through to step #3 in case the framework is also specified in the additionalFrameworks section
+ app_config = this;
+ }
+
+ // Step #3: apply the values from "additionalFrameworks"
+ auto overrides = app_config->m_additional_frameworks.find(m_fx_name);
+ if (overrides != app_config->m_additional_frameworks.end())
+ {
+ set_effective_values(overrides->second);
+ }
+ }
}
}
catch (const std::exception& je)
trace::error(_X("A JSON parsing exception occurred in [%s]: %s"), m_path.c_str(), jes.c_str());
return false;
}
- return true;
+
+ return rc;
}
const pal::string_t& runtime_config_t::get_tfm() const
return m_patch_roll_fwd;
}
-bool runtime_config_t::get_prerelease_roll_fwd() const
-{
- assert(m_valid);
- return m_prerelease_roll_fwd;
-}
-
roll_fwd_on_no_candidate_fx_option runtime_config_t::get_roll_fwd_on_no_candidate_fx() const
{
assert(m_valid);
return m_roll_fwd_on_no_candidate_fx;
}
-void runtime_config_t::set_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option value)
+void runtime_config_t::force_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option value)
{
assert(m_valid);
m_roll_fwd_on_no_candidate_fx = value;
+ m_fx_readonly.set_roll_fwd_on_no_candidate_fx(value);
}
bool runtime_config_t::get_portable() const
#include "cpprest/json.h"
typedef web::json::value json_value;
+typedef web::json::object json_object;
enum class roll_fwd_on_no_candidate_fx_option
{
major_or_minor
};
+class runtime_config_framework_t
+{
+public:
+ // Uses a "nullable<T>" pattern until we add such a type
+
+ runtime_config_framework_t()
+ : has_fx_ver(false)
+ , has_roll_fwd_on_no_candidate_fx(false)
+ , has_patch_roll_fwd(false)
+ , fx_ver(_X(""))
+ , patch_roll_fwd(false)
+ , roll_fwd_on_no_candidate_fx((roll_fwd_on_no_candidate_fx_option)0)
+ { }
+
+ const pal::string_t* get_fx_ver() const
+ {
+ return (has_fx_ver ? &fx_ver : nullptr);
+ }
+ void set_fx_ver(pal::string_t value)
+ {
+ has_fx_ver = true;
+ fx_ver = value;
+ }
+
+ const bool* get_patch_roll_fwd() const
+ {
+ return (has_patch_roll_fwd ? &patch_roll_fwd : nullptr);
+ }
+ void set_patch_roll_fwd(bool value)
+ {
+ has_patch_roll_fwd = true;
+ patch_roll_fwd = value;
+ }
+
+ const roll_fwd_on_no_candidate_fx_option* get_roll_fwd_on_no_candidate_fx() const
+ {
+ return (has_roll_fwd_on_no_candidate_fx ? &roll_fwd_on_no_candidate_fx : nullptr);
+ }
+ void set_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option value)
+ {
+ has_roll_fwd_on_no_candidate_fx = true;
+ roll_fwd_on_no_candidate_fx = value;
+ }
+
+private:
+ bool has_fx_ver;
+ bool has_patch_roll_fwd;
+ bool has_roll_fwd_on_no_candidate_fx;
+
+ pal::string_t fx_ver;
+ bool patch_roll_fwd;
+ roll_fwd_on_no_candidate_fx_option roll_fwd_on_no_candidate_fx;
+};
+
class runtime_config_t
{
public:
runtime_config_t();
- void parse(const pal::string_t& path, const pal::string_t& dev_path, const runtime_config_t* defaults);
+ void parse(const pal::string_t& path, const pal::string_t& dev_path, const runtime_config_t* higher_layer_config, const runtime_config_t* app_config);
bool is_valid() const { return m_valid; }
const pal::string_t& get_path() const { return m_path; }
const pal::string_t& get_dev_path() const { return m_dev_path; }
const pal::string_t& get_fx_version() const;
- void set_fx_version(const pal::string_t& value);
const pal::string_t& get_fx_name() const;
const pal::string_t& get_tfm() const;
const std::list<pal::string_t>& get_probe_paths() const;
bool get_patch_roll_fwd() const;
- bool get_prerelease_roll_fwd() const;
roll_fwd_on_no_candidate_fx_option get_roll_fwd_on_no_candidate_fx() const;
- void set_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option value);
+ void force_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option value);
bool get_portable() const;
bool parse_opts(const json_value& opts);
void combine_properties(std::unordered_map<pal::string_t, pal::string_t>& combined_properties) const;
private:
- bool ensure_parsed();
+ bool ensure_parsed(const runtime_config_t* defaults);
bool ensure_dev_config_parsed();
std::unordered_map<pal::string_t, pal::string_t> m_properties;
+ std::unordered_map<pal::string_t, runtime_config_framework_t> m_additional_frameworks;
+ runtime_config_framework_t m_fx_global; // the settings that will be applied to the next lower layer; does not include version (Step #1)
+ runtime_config_framework_t m_fx; // the settings in the current "framework" section (Step #3)
+ runtime_config_framework_t m_fx_readonly; // the settings that can't be changed by lower layers (Step #4)
std::vector<std::string> m_prop_keys;
std::vector<std::string> m_prop_values;
std::list<pal::string_t> m_probe_paths;
+
pal::string_t m_tfm;
pal::string_t m_fx_name;
+
+ // These are the effective settings
pal::string_t m_fx_ver;
bool m_patch_roll_fwd;
- bool m_prerelease_roll_fwd;
roll_fwd_on_no_candidate_fx_option m_roll_fwd_on_no_candidate_fx;
pal::string_t m_dev_path;
pal::string_t m_path;
bool m_portable;
bool m_valid;
+
+private:
+ bool parse_framework(const json_object& fx_obj);
+ void set_effective_values(const runtime_config_framework_t& overrides);
+ static void copy_framework_settings_to(const runtime_config_framework_t& from, runtime_config_framework_t& to);
+
};
#endif // __RUNTIME_CONFIG_H__
\ No newline at end of file
}
[Fact]
- public void Multiple_SharedFxLookup_Propagated_RuntimeConfig_Values()
+ public void Multiple_SharedFxLookup_Propagated_Global_RuntimeConfig_Values()
{
var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
.Copy();
}
[Fact]
- public void Multiple_SharedFxLookup_UberFx_Deps_Overrides_NetCoreApp()
+ public void Multiple_SharedFxLookup_Propagated_Additional_Framework_RuntimeConfig_Values()
{
var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
.Copy();
var appDll = fixture.TestProject.AppDll;
string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
- SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true);
+
+ var additionalfxs = new JArray();
+ additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.0", applyPatches: false, rollForwardOnNoCandidateFx: 0));
+ SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, additionalFrameworks : additionalfxs);
// Add versions in the exe folders
- AddAvailableSharedFxVersions(_exeSharedFxBaseDir, "9999.0.0");
- AddAvailableSharedUberFxVersions(_exeSharedUberFxBaseDir, "9999.0.0", null, "7777.0.0");
+ AddAvailableSharedFxVersions(_exeSharedFxBaseDir, "9999.1.0");
+ AddAvailableSharedUberFxVersions(_exeSharedUberFxBaseDir, "9999.5.5", "UberValue", "7777.0.0");
- // The System.Collections.Immutable.dll is located in the UberFramework and NetCoreApp
- // The System.Collections.dll is only located in NetCoreApp
- // Version: NetCoreApp 9999.0.0
+ // Version: NetCoreApp 9999.5.5 (in framework section)
+ // NetCoreApp 9999.1.0 (in app's additionalFrameworks section)
// UberFramework 7777.0.0
- // Exe: NetCoreApp 9999.0.0
+ // Exe: NetCoreApp 9999.1.0
// UberFramework 7777.0.0
- // Expected: 9999.0.0
+ // Expected: 9999.1.0
// 7777.0.0
dotnet.Exec(appDll)
.WorkingDirectory(_currentWorkingDir)
.Should()
.Pass()
.And
- .HaveStdErrContaining(Path.Combine("7777.0.0", "System.Collections.Immutable.dll"))
+ .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.0"))
+ .And
+ .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0"));
+
+ // Change the additionalFrameworks to allow roll forward, overriding Uber's global section and ignoring Uber's framework section
+ additionalfxs.Clear();
+ additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.0.0", applyPatches: false, rollForwardOnNoCandidateFx: 1));
+ additionalfxs.Add(GetAdditionalFramework("UberFx", "7777.0.0", applyPatches: false, rollForwardOnNoCandidateFx: 0));
+ SetRuntimeConfigJson(runtimeConfig, "7777.0.0", rollFwdOnNoCandidateFx:0, useUberFramework: true, additionalFrameworks: additionalfxs);
+
+ // Version: NetCoreApp 9999.5.5 (in framework section)
+ // NetCoreApp 9999.0.0 (in app's additionalFrameworks section)
+ // UberFramework 7777.0.0
+ // UberFramework 7777.0.0 (in app's additionalFrameworks section)
+ // 'Roll forward on no candidate fx' disabled through env var
+ // 'Roll forward on no candidate fx' disabled through Uber's global runtimeconfig
+ // 'Roll forward on no candidate fx' enabled for NETCore.App enabled through additionalFrameworks section
+ // Exe: NetCoreApp 9999.1.0
+ // UberFramework 7777.0.0
+ // Expected: 9999.1.0
+ // 7777.0.0
+ dotnet.Exec(appDll)
+ .WorkingDirectory(_currentWorkingDir)
+ .EnvironmentVariable("COREHOST_TRACE", "1")
+ .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0")
+ .CaptureStdOut()
+ .CaptureStdErr()
+ .Execute()
+ .Should()
+ .Pass()
.And
- .HaveStdErrContaining(Path.Combine("9999.0.0", "System.Collections.dll"))
+ .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.0"))
.And
- .NotHaveStdErrContaining(Path.Combine("9999.0.0", "System.Collections.Immutable.dll"));
+ .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0"));
- DeleteAvailableSharedFxVersions(_exeSharedFxBaseDir, "9999.0.0");
+ // Same as previous except use of '--roll-forward-on-no-candidate-fx'
+ // Expected: Fail since '--roll-forward-on-no-candidate-fx' should apply to all layers
+ dotnet.Exec(
+ "exec",
+ "--roll-forward-on-no-candidate-fx", "0",
+ appDll)
+ .WorkingDirectory(_currentWorkingDir)
+ .EnvironmentVariable("COREHOST_TRACE", "1")
+ .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "1")
+ .CaptureStdOut()
+ .CaptureStdErr()
+ .Execute()
+ .Should()
+ .Fail()
+ .And
+ .HaveStdErrContaining("It was not possible to find any compatible framework version");
+
+ DeleteAvailableSharedFxVersions(_exeSharedFxBaseDir, "9999.1.0");
DeleteAvailableSharedFxVersions(_exeSharedUberFxBaseDir, "7777.0.0");
}
* }
* }
*/
- private void SetRuntimeConfigJson(string destFile, string version, int? rollFwdOnNoCandidateFx = null, string testConfigPropertyValue = null, bool? useUberFramework = false)
+ private void SetRuntimeConfigJson(string destFile, string version, int? rollFwdOnNoCandidateFx = null, string testConfigPropertyValue = null, bool? useUberFramework = false, JArray additionalFrameworks = null)
{
string name = useUberFramework.HasValue && useUberFramework.Value ? "Microsoft.UberFramework" : "Microsoft.NETCore.App";
);
}
+ if (additionalFrameworks != null)
+ {
+ runtimeOptions.Add("additionalFrameworks", additionalFrameworks);
+ }
+
FileInfo file = new FileInfo(destFile);
if (!file.Directory.Exists)
{
File.WriteAllText(destFile, json.ToString());
}
+ static private JObject GetAdditionalFramework(string fxName, string fxVersion, bool? applyPatches, int? rollForwardOnNoCandidateFx)
+ {
+ var jobject = new JObject(new JProperty("name", fxName));
+
+ if (fxVersion != null)
+ {
+ jobject.Add(new JProperty("version", fxVersion));
+ }
+
+ if (applyPatches.HasValue)
+ {
+ jobject.Add(new JProperty("applyPatches", applyPatches.Value));
+ }
+
+ if (rollForwardOnNoCandidateFx.HasValue)
+ {
+ jobject.Add(new JProperty("rollForwardOnNoCandidateFx", rollForwardOnNoCandidateFx));
+ }
+
+ return jobject;
+ }
+
static private void CreateUberFrameworkArtifacts(string builtSharedFxDir, string builtSharedUberFxDir, string assemblyVersion = null, string fileVersion = null)
{
DirectoryInfo dir = new DirectoryInfo(builtSharedUberFxDir);