Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use MSBuild to find target framework #348

Merged
merged 7 commits into from
Nov 26, 2024
Merged

Use MSBuild to find target framework #348

merged 7 commits into from
Nov 26, 2024

Conversation

gcbeattyAWS
Copy link
Contributor

@gcbeattyAWS gcbeattyAWS commented Nov 18, 2024

Issue #, if available: #211

Description of changes:

When packaging a lambda using dotnet lambda package, if the target project does not include a node (because it is defined in a Directory.build.props at a higher location) the packaging tool fails saying it does not know what framework to use.

Missing required parameter: --framework

Expected Behavior

The tool will understand that MSBuild can walk directories searching for Directory.build.props files to define these values.

Current Behavior

The tool fails to walk the folder hierarchy to find the values necessary for the build.

Changes

  1. Creates a generic helper function to get properties for a project using MSbuild and then XML parsing of the csproj as a fallback, https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2022
  2. Updates find framework function to use above function
  3. Updates other existing functions to use the generic function

Testing

  1. Created new lambda function which has the TargetFramework defined in Directory.Build.props instead of the .csproject file. I then added this to the existing unit test case to verify the target framework is read correctly.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@gcbeattyAWS gcbeattyAWS changed the base branch from master to dev November 18, 2024 18:17
@gcbeattyAWS gcbeattyAWS marked this pull request as ready for review November 18, 2024 20:34
string error = process.StandardError.ReadToEnd();
process.WaitForExit();

if (process.ExitCode != 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the process failed we should fallback to the original XML parsing logic. There could be CI systems that are still deploying .NET 6 and only have .NET 6 SDK installed. The -getoutput command was added in .NET 8.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah good point! i will update accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in latest commit


var xdoc = XDocument.Load(projectFile);
var arguments = new[]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We look up TargetFramework, OutputType, and PublishAot from the project file in this class. I would like to see this logic generalized so that we can have a method that takes in the project and a params of property names and returns back a dictionary of values. I don't really want 3 different code paths starting the dotnet process which will end up drifting.

The shared method should have the fallback logic I describe below that if the process fails parse the project xml like it does today so we don't risk any breaking behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure will update!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in latest commit

@@ -0,0 +1,49 @@
# AWS Lambda Empty Function Project
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't really need the readme for checked in for the test apps.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in latest commit

var hasExited = process.WaitForExit(5000);
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep the 5 second timeout or at least some timeout? I'm thinking of @philasmar recent PR that at least added int.MaxValue to avoid deadlocks in this API. #342

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good i will update to 5 seconds

{
msbuildProcessFailed = true;
Console.WriteLine($"MSBuild process exited with code {process.ExitCode}. Error: {error}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No Console.WriteLine calls are used. This same code is used inside Visual Studio and there is no console there. If you want to log something you need to pass in an instance if IToolLogger to handle logging.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too sure we should be logging anything visible to the user here. We are exposing an implementation detail that would be more confusing if anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i just removed the logging all together

{
// swallow any exceptions related to `dotnet msbuild`
msbuildProcessFailed = true;
Console.WriteLine($"Error executing MSBuild or parsing output: {ex.Message}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No Console.WriteLine calls are used. This same code is used inside Visual Studio and there is no console there. If you want to log something you need to pass in an instance if IToolLogger to handle logging.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about logging

}
catch (Exception ex)
{
Console.WriteLine($"Error parsing project file XML: {ex.Message}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No Console.WriteLine calls are used. This same code is used inside Visual Studio and there is no console there. If you want to log something you need to pass in an instance if IToolLogger to handle logging.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about logging

process.Kill();
msbuildProcessFailed = true;
}
using JsonDocument doc = JsonDocument.Parse(output);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something but if propertyNames is a single value isn't the output of the command just the value and not the JSON document?

Copy link
Contributor Author

@gcbeattyAWS gcbeattyAWS Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS C:\dev\repos\aws-extensions-for-dotnet-cli> dotnet msbuild .\testapps\TestFunction\TestFunction.csproj  --getProperty:TargetFramework,TargetFrameworks
{
  "Properties": {
    "TargetFramework": "net6.0",
    "TargetFrameworks": ""
  }
}

is what the commmand returns, so it is a json document

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check if the count of propertyNames is 1 or greater since the result varies. If you run:

dotnet msbuild --nologo --getProperty:TargetFramework

The result will be

net8.0

If you run

dotnet msbuild --nologo --getProperty:TargetFramework,TargetFrameworks

The result will be:

{
  "Properties": {
    "TargetFramework": "net8.0",
    "TargetFrameworks": ""
  }
}

You need to handle both cases

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes you are right. i will update that!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to handle case for when only one property is provided. Thanks for catching this!

"Name": "Amazon.Lambda.Tools",
"Type": "Patch",
"ChangelogMessages": [
"Use MSBuild to find target framework"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add more explanation here for the significance of this for customers. Not sure everyone would know what this implies.

/// <summary>
/// Retrieve the `OutputType` property of a given project
// <summary>
/// Looks up specified properties from a project file.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: drop "file" since MSBUILD is not looking at the project file (.csproj) alone

process.Kill();
msbuildProcessFailed = true;
}
using JsonDocument doc = JsonDocument.Parse(output);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check if the count of propertyNames is 1 or greater since the result varies. If you run:

dotnet msbuild --nologo --getProperty:TargetFramework

The result will be

net8.0

If you run

dotnet msbuild --nologo --getProperty:TargetFramework,TargetFrameworks

The result will be:

{
  "Properties": {
    "TargetFramework": "net8.0",
    "TargetFrameworks": ""
  }
}

You need to handle both cases

{
msbuildProcessFailed = true;
Console.WriteLine($"MSBuild process exited with code {process.ExitCode}. Error: {error}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too sure we should be logging anything visible to the user here. We are exposing an implementation detail that would be more confusing if anything.

{
// swallow any exceptions related to `dotnet msbuild`
msbuildProcessFailed = true;
Console.WriteLine($"Error executing MSBuild or parsing output: {ex.Message}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about logging

}
catch (Exception ex)
{
Console.WriteLine($"Error parsing project file XML: {ex.Message}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about logging

Copy link
Member

@normj normj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One minor comment to address an existing issue but I don't need to review the PR again assuming you address the comment.

src/Amazon.Common.DotNetCli.Tools/Utilities.cs Outdated Show resolved Hide resolved
@boblodgett boblodgett requested a review from philasmar November 21, 2024 18:31
@gcbeattyAWS gcbeattyAWS merged commit b8b53d1 into dev Nov 26, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants