Skip to content
This repository was archived by the owner on Apr 20, 2023. It is now read-only.

Commit 0ba2e1f

Browse files
committed
Initial add of launchSettings.json support
1 parent c94fe61 commit 0ba2e1f

File tree

6 files changed

+220
-0
lines changed

6 files changed

+220
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.DotNet.Cli.Utils;
2+
using Newtonsoft.Json.Linq;
3+
4+
namespace Microsoft.DotNet.Tools.Run.LaunchSettings
5+
{
6+
public interface ILaunchSettingsProvider
7+
{
8+
string CommandName { get; }
9+
10+
bool TryApplySettings(JObject document, JObject model, ref ICommand command, out string runAfterLaunch);
11+
}
12+
13+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Microsoft.DotNet.Cli.Utils;
5+
using Newtonsoft.Json.Linq;
6+
7+
namespace Microsoft.DotNet.Tools.Run.LaunchSettings
8+
{
9+
internal class LaunchSettingsManager
10+
{
11+
private const string ProfilesKey = "profiles";
12+
private const string CommandNameKey = "commandName";
13+
private const string DefaultProfileCommandName = "Project";
14+
private static IReadOnlyDictionary<string, ILaunchSettingsProvider> _providers;
15+
16+
static LaunchSettingsManager()
17+
{
18+
_providers = new Dictionary<string, ILaunchSettingsProvider>
19+
{
20+
{ ProjectLaunchSettingsProvider.CommandNameValue, new ProjectLaunchSettingsProvider() }
21+
};
22+
}
23+
24+
public static bool TryApplyLaunchSettings(string launchSettingsJsonContents, ref ICommand command, out string runAfterLaunch, string profileName = null)
25+
{
26+
try
27+
{
28+
var model = JObject.Parse(launchSettingsJsonContents);
29+
var profilesObject = model[ProfilesKey] as JObject;
30+
31+
if (profilesObject == null)
32+
{
33+
runAfterLaunch = null;
34+
return false;
35+
}
36+
37+
JObject profileObject;
38+
if (profileName == null)
39+
{
40+
profileObject = profilesObject
41+
.Properties()
42+
.FirstOrDefault(IsDefaultProfileType)?.Value as JObject;
43+
}
44+
else
45+
{
46+
profileObject = profilesObject[profileName] as JObject;
47+
}
48+
49+
if (profileObject == null)
50+
{
51+
foreach (var prop in profilesObject.Properties())
52+
{
53+
var profile = prop.Value as JObject;
54+
55+
if (profile != null)
56+
{
57+
var cmdName = profile[CommandNameKey]?.Value<string>();
58+
if (_providers.ContainsKey(cmdName))
59+
{
60+
profileObject = profile;
61+
break;
62+
}
63+
}
64+
}
65+
}
66+
67+
var commandName = profileObject?[CommandNameKey]?.Value<string>();
68+
69+
if (profileObject == null || !TryLocateHandler(commandName, out ILaunchSettingsProvider provider))
70+
{
71+
runAfterLaunch = null;
72+
return false;
73+
}
74+
75+
return provider.TryApplySettings(model, profileObject, ref command, out runAfterLaunch);
76+
}
77+
catch
78+
{
79+
runAfterLaunch = null;
80+
return false;
81+
}
82+
}
83+
84+
private static bool TryLocateHandler(string commandName, out ILaunchSettingsProvider provider)
85+
{
86+
return _providers.TryGetValue(commandName, out provider);
87+
}
88+
89+
private static bool IsDefaultProfileType(JProperty profileProperty)
90+
{
91+
JObject profile = profileProperty.Value as JObject;
92+
var commandName = profile?[CommandNameKey]?.Value<string>();
93+
return string.Equals(commandName, DefaultProfileCommandName, StringComparison.Ordinal);
94+
}
95+
}
96+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.DotNet.Cli.Utils;
4+
using Newtonsoft.Json.Linq;
5+
6+
namespace Microsoft.DotNet.Tools.Run.LaunchSettings
7+
{
8+
public class ProjectLaunchSettingsProvider : ILaunchSettingsProvider
9+
{
10+
public static readonly string CommandNameValue = "Project";
11+
12+
public string CommandName => CommandNameValue;
13+
14+
public bool TryApplySettings(JObject document, JObject model, ref ICommand command, out string runAfterLaunch)
15+
{
16+
try
17+
{
18+
var config = model.ToObject<ProjectLaunchSettingsModel>();
19+
20+
//For now, ignore everything but the environment variables section
21+
22+
foreach (var entry in config.EnvironmentVariables)
23+
{
24+
string value = Environment.ExpandEnvironmentVariables(entry.Value);
25+
//NOTE: MSBuild variables are not expanded like they are in VS
26+
command.EnvironmentVariable(entry.Key, value);
27+
}
28+
29+
runAfterLaunch = null;
30+
return true;
31+
}
32+
catch
33+
{
34+
runAfterLaunch = null;
35+
return false;
36+
}
37+
}
38+
39+
private class ProjectLaunchSettingsModel
40+
{
41+
public ProjectLaunchSettingsModel()
42+
{
43+
EnvironmentVariables = new Dictionary<string, string>();
44+
}
45+
46+
public string CommandLineArgs { get; set; }
47+
48+
public bool LaunchBrowser { get; set; }
49+
50+
public string LaunchUrl { get; set; }
51+
52+
public string ApplicationUrl { get; set; }
53+
54+
public Dictionary<string, string> EnvironmentVariables { get; }
55+
}
56+
}
57+
}

src/dotnet/commands/dotnet-run/LocalizableStrings.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ internal class LocalizableStrings
1717

1818
public const string CommandOptionProjectDescription = "The path to the project file to run (defaults to the current directory if there is only one project).";
1919

20+
public const string CommandOptionLaunchProfileDescription = "The name of the launch profile (if any) to use when launching the application.";
21+
22+
public const string CommandOptionNoLaunchProfileDescription = "Do not attempt to use launchSettings.json to configure the application.";
23+
2024
public const string RunCommandException = "The build failed. Please fix the build errors and run again.";
2125

2226
public const string RunCommandExceptionUnableToRunSpecifyFramework = "Unable to run your project\nYour project targets multiple frameworks. Please specify which framework to run using '{0}'.";
@@ -28,5 +32,11 @@ internal class LocalizableStrings
2832
public const string RunCommandExceptionMultipleProjects = "Specify which project file to use because {0} contains more than one project file.";
2933

3034
public const string RunCommandAdditionalArgsHelpText = "Arguments passed to the application that is being run.";
35+
36+
public const string RunCommandExceptionCouldNotLocateALaunchSettingsFile = "The specified launch profile could not be located.";
37+
38+
public const string RunCommandExceptionCouldNotApplyLaunchSettings = "The launch profile \"{0}\" could not be applied.";
39+
40+
public const string DefaultLaunchProfileDisplayName = "(Default)";
3141
}
3242
}

src/dotnet/commands/dotnet-run/RunCommand.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Microsoft.Build.Evaluation;
99
using Microsoft.DotNet.Cli.Utils;
1010
using Microsoft.DotNet.Tools.MSBuild;
11+
using Microsoft.DotNet.Tools.Run.LaunchSettings;
1112

1213
namespace Microsoft.DotNet.Tools.Run
1314
{
@@ -22,6 +23,10 @@ public partial class RunCommand
2223
private List<string> _args;
2324
private bool ShouldBuild => !NoBuild;
2425

26+
public string LaunchProfile { get; private set; }
27+
public bool NoLaunchProfile { get; private set; }
28+
29+
2530
public int Start()
2631
{
2732
Initialize();
@@ -33,6 +38,27 @@ public int Start()
3338

3439
ICommand runCommand = GetRunCommand();
3540

41+
if (!NoLaunchProfile)
42+
{
43+
var buildPathContainer = File.Exists(Project) ? Path.GetDirectoryName(Project) : Project;
44+
var launchSettingsPath = Path.Combine(buildPathContainer, "Properties", "launchSettings.json");
45+
if (File.Exists(launchSettingsPath))
46+
{
47+
var launchSettingsFileContents = File.ReadAllText(launchSettingsPath);
48+
if (!LaunchSettingsManager.TryApplyLaunchSettings(launchSettingsFileContents, ref runCommand, out string runAfterLaunch, LaunchProfile))
49+
{
50+
string profileName = string.IsNullOrEmpty(LaunchProfile) ? LocalizableStrings.DefaultLaunchProfileDisplayName : LaunchProfile;
51+
//Error that the launch profile couldn't be applied
52+
Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, profileName));
53+
}
54+
}
55+
else if (!string.IsNullOrEmpty(LaunchProfile))
56+
{
57+
//Error that the launch profile couldn't be found
58+
Reporter.Error.WriteLine(LocalizableStrings.RunCommandExceptionCouldNotLocateALaunchSettingsFile);
59+
}
60+
}
61+
3662
return runCommand
3763
.Execute()
3864
.ExitCode;
@@ -42,26 +68,34 @@ public RunCommand(string configuration,
4268
string framework,
4369
bool noBuild,
4470
string project,
71+
string launchProfile,
72+
bool noLaunchProfile,
4573
IReadOnlyCollection<string> args)
4674
{
4775
Configuration = configuration;
4876
Framework = framework;
4977
NoBuild = noBuild;
5078
Project = project;
79+
LaunchProfile = launchProfile;
80+
NoLaunchProfile = noLaunchProfile;
5181
Args = args;
5282
}
5383

5484
public RunCommand MakeNewWithReplaced(string configuration = null,
5585
string framework = null,
5686
bool? noBuild = null,
5787
string project = null,
88+
string launchProfile = null,
89+
bool? noLaunchProfile = null,
5890
IReadOnlyCollection<string> args = null)
5991
{
6092
return new RunCommand(
6193
configuration ?? this.Configuration,
6294
framework ?? this.Framework,
6395
noBuild ?? this.NoBuild,
6496
project ?? this.Project,
97+
launchProfile ?? this.LaunchProfile,
98+
noLaunchProfile ?? this.NoLaunchProfile,
6599
args ?? this.Args
66100
);
67101
}

src/dotnet/commands/dotnet-run/RunCommandParser.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public static Command Run() =>
2121
framework: o.SingleArgumentOrDefault("--framework"),
2222
noBuild: o.HasOption("--no-build"),
2323
project: o.SingleArgumentOrDefault("--project"),
24+
launchProfile: o.SingleArgumentOrDefault("--launch-profile"),
25+
noLaunchProfile: o.HasOption("--no-launch-profile"),
2426
args: o.Arguments
2527
)),
2628
options: new[]
@@ -32,6 +34,14 @@ public static Command Run() =>
3234
"-p|--project",
3335
LocalizableStrings.CommandOptionProjectDescription,
3436
Accept.ExactlyOneArgument()),
37+
Create.Option(
38+
"--launch-profile",
39+
LocalizableStrings.CommandOptionLaunchProfileDescription,
40+
Accept.ExactlyOneArgument()),
41+
Create.Option(
42+
"--no-launch-profile",
43+
LocalizableStrings.CommandOptionNoLaunchProfileDescription,
44+
Accept.NoArguments()),
3545
Create.Option(
3646
"--no-build",
3747
LocalizableStrings.CommandOptionNoBuildDescription,

0 commit comments

Comments
 (0)