Skip to content

Commit 0a70272

Browse files
committed
Improve automatic restore support in SDK mode
Also provide better diagnostics when package is used instead of SDK mode.
1 parent d5e8683 commit 0a70272

13 files changed

Lines changed: 85 additions & 78 deletions

.netconfig

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,7 @@
9191
sha = 4339749ef4b8f66def75931df09ef99c149f8421
9292
[file "src/kzu.snk"]
9393
url = https://github.com/devlooped/oss/blob/main/src/kzu.snk
94-
etag = b8d789b5b6bea017cdcc8badcea888ad78de3e34298efca922054e9fb0e7b6b9
95-
weak
96-
sha = 0683ee777d7d878d4bf013d7deea352685135a05
94+
skip
9795
[file ".github/workflows/pages.yml"]
9896
url = https://github.com/clarius/pages/blob/main/.github/workflows/pages.yml
9997
etag = c52b3f0463b88abf696ddf2c6902675e0bc9d1e812bb317cb758221d75330b56

readme.md

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ modify any of the others since they automatically become *None* items.
6262

6363
> [!TIP]
6464
> An initial build after selection change migh be needed to restore the packages and compile the
65-
> selected file
65+
> selected file, unless you're using the SDK mode for SmallSharp (see below).
6666
6767
All compile files directly under the project directory root are considered top-level programs for
6868
selection and compilation purposes. If you need to share code among them, you can place additional
@@ -93,10 +93,16 @@ and adding a couple extra properties to the project file:
9393
</Project>
9494
```
9595

96-
If your file-based apps use the `#:sdk` [directive](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives#file-based-apps),
97-
you need to add SmallSharp as an SDK reference instead so the SDK is picked up by the
98-
generated targets/props instead of the project file. You also don't need the additional
99-
properties since the SDK mode sets them automatically for you:
96+
There are some limitations with this mode, however:
97+
* You cannot use the `#:sdk` [directive](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives#file-based-apps)
98+
to specify a different SDK per file, since the project file already specifies one.
99+
* CLI-based builds may require multiple passes to restore and build the selected file, since
100+
the package is only restored after the first build.
101+
* You must add ImportProjectExtensionProps/ImportProjectExtensionTargets manually, polluting the
102+
project file.
103+
104+
So the recommended way to use SmallSharp is via the SDK mode, which results in a more streamlined
105+
and seamless experience across IDE and CLI builds:
100106

101107
```xml
102108
<Project Sdk="SmallSharp/2.1.0">
@@ -109,6 +115,9 @@ properties since the SDK mode sets them automatically for you:
109115
</Project>
110116
```
111117

118+
The SDK mode will always produce a successful build in a single `dotnet build` pass even if you
119+
change the `ActiveFile` between builds.
120+
112121
> [!IMPORTANT]
113122
> If no `#:sdk` directive is provided by a specific C# file-based app, the `Microsoft.NET.SDK` will be
114123
> used by default in this SDK mode.
@@ -157,19 +166,6 @@ since the "Main" file selection is performed exclusively via MSBuild item manipu
157166
> SDK reference, and do all project/package references in the top-level files using the `#:package` and
158167
> `#:property` directives for improved isolation between the different file-based apps.
159168
160-
```xml
161-
<Project Sdk="Microsoft.NET.Sdk">
162-
163-
<Sdk Name="SmallSharp" Version="2.0.0" />
164-
165-
<PropertyGroup>
166-
<OutputType>Exe</OutputType>
167-
<TargetFramework>net10.0</TargetFramework>
168-
</PropertyGroup>
169-
170-
</Project>
171-
```
172-
173169
![run humanizer file](https://raw.githubusercontent.com/devlooped/SmallSharp/main/assets/img/runfile1.png)
174170

175171
![run mcp file](https://raw.githubusercontent.com/devlooped/SmallSharp/main/assets/img/runfile2.png)

src/Demo/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Demo.sln

src/Demo/global.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"msbuild-sdks": {
3-
"SmallSharp": "2.0.0"
3+
"SmallSharp": "42.537.1166"
44
}
5-
}
5+
}

src/SmallSharp/EmitTargets.cs

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34
using System.Linq;
45
using System.Text.RegularExpressions;
56
using System.Xml;
67
using System.Xml.Linq;
78
using Microsoft.Build.Framework;
89
using Microsoft.Build.Utilities;
10+
using static SmallSharp.TaskItemFactory;
911

1012
namespace SmallSharp;
1113

@@ -27,9 +29,6 @@ public class EmitTargets : Task
2729
[Required]
2830
public required string TargetsFile { get; set; }
2931

30-
[Required]
31-
public bool UsingSmallSharpSDK { get; set; } = false;
32-
3332
[Output]
3433
public ITaskItem[] Packages { get; set; } = [];
3534

@@ -39,9 +38,6 @@ public class EmitTargets : Task
3938
[Output]
4039
public ITaskItem[] Properties { get; set; } = [];
4140

42-
[Output]
43-
public bool Success { get; set; } = false;
44-
4541
public override bool Execute()
4642
{
4743
if (StartupFile is null)
@@ -65,10 +61,7 @@ public override bool Execute()
6561
var id = match.Groups[1].Value.Trim();
6662
var version = match.Groups[2].Value.Trim();
6763

68-
packages.Add(new TaskItem(id, new Dictionary<string, string>
69-
{
70-
{ "Version", version }
71-
}));
64+
packages.Add(NewTaskItem(id, [("Version", version)]));
7265

7366
items.Add(new XElement("PackageReference",
7467
new XAttribute("Include", id),
@@ -80,10 +73,7 @@ public override bool Execute()
8073
var version = sdkMatch.Groups[2].Value.Trim();
8174
if (!string.IsNullOrEmpty(version))
8275
{
83-
sdkItems.Add(new TaskItem(name, new Dictionary<string, string>
84-
{
85-
{ "Version", version }
86-
}));
76+
sdkItems.Add(NewTaskItem(name, [("Version", version)]));
8777
sdks.Add([new XAttribute("Sdk", name), new XAttribute("Version", version)]);
8878
}
8979
else
@@ -97,10 +87,7 @@ public override bool Execute()
9787
var name = propMatch.Groups[1].Value.Trim();
9888
var value = propMatch.Groups[2].Value.Trim();
9989

100-
propItems.Add(new TaskItem(name, new Dictionary<string, string>
101-
{
102-
{ "Value", value }
103-
}));
90+
propItems.Add(NewTaskItem(name, [("Value", value)]));
10491
properties.Add(new XElement(name, value));
10592
}
10693
}
@@ -109,12 +96,6 @@ public override bool Execute()
10996
Sdks = [.. sdkItems];
11097
Properties = [.. propItems];
11198

112-
if (sdks.Count > 0 && !UsingSmallSharpSDK)
113-
{
114-
Log.LogError($"When using #:sdk directive(s), you must use SmallSharp as an SDK: <Project Sdk=\"SmallSharp/{ThisAssembly.Project.Version}\">.");
115-
return false;
116-
}
117-
11899
// We only emit the default SDK if the SmallSharpSDK is in use, since otherwise the
119100
// project file is expected to define its own SDK and we'd be duplicating it.
120101
if (sdks.Count == 0)
@@ -131,14 +112,18 @@ public override bool Execute()
131112
WriteXml(Path.Combine(BaseIntermediateOutputPath, "SmallSharp.sdk.targets"), new XElement("Project",
132113
sdks.Select(x => new XElement("Import", [new XAttribute("Project", "Sdk.targets"), .. x]))));
133114

134-
WriteXml(PropsFile, new XElement("Project"));
115+
WriteXml(PropsFile, new XElement("Project",
116+
new XElement("PropertyGroup",
117+
[new XElement("SmallSharpProjectExtensionPropsImported", "true")])));
135118

136-
Success = true;
137119
return true;
138120
}
139121

140122
void WriteXml(string path, XElement root)
141123
{
124+
if (Path.GetDirectoryName(path) is { } dir)
125+
Directory.CreateDirectory(dir);
126+
142127
using var writer = XmlWriter.Create(path, new XmlWriterSettings { Indent = true });
143128
root.Save(writer);
144129
}

src/SmallSharp/Sdk.targets

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
<Import Project="..\build\SmallSharp.targets" />
1010

11-
<Target Name="ImplicitPackageReferenceFromStartupFile" BeforeTargets="_GenerateProjectRestoreGraphPerFramework"
11+
<Target Name="ImplicitPackageReferenceFromStartupFile" BeforeTargets="_GenerateProjectRestoreGraphPerFramework;ResolvePackageAssets"
1212
DependsOnTargets="StartupFile"
13-
Condition="'$(StartupFile)' != '' and Exists('$(StartupFile)') and '$(RestoreNeeded)' == 'true'" >
13+
Condition="'$(StartupFile)' != '' and Exists('$(StartupFile)') and '$(SmallSharpProjectExtensionPropsImported)' != 'true'">
1414

1515
<!-- Optimize for restore success on first run without previously running our targets -->
1616
<ReadLinesFromFile File="$(StartupFile)">
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<Project>
2+
<PropertyGroup>
3+
<SmallSharpVersion>42.42.42</SmallSharpVersion>
4+
</PropertyGroup>
5+
</Project>

src/SmallSharp/SmallSharp.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
<ItemGroup>
3636
<None Include="..\_._" PackFolder="lib\netstandard2.0" Visible="false" />
37+
<None Update="SmallSharp.props" PackFolder="$(PackFolder)" CopyToOutputDirectory="PreserveNewest" />
38+
<None Update="SmallSharp.Version.props" PackFolder="$(PackFolder)" CopyToOutputDirectory="PreserveNewest" />
3739
<None Update="SmallSharp.targets" PackFolder="$(PackFolder)" CopyToOutputDirectory="PreserveNewest" />
3840
<None Update="SmallSharp.Before.targets" PackFolder="$(PackFolder)" CopyToOutputDirectory="PreserveNewest" />
3941
<None Update="Sdk.*" PackFolder="Sdk" CopyToOutputDirectory="PreserveNewest" />
@@ -46,4 +48,11 @@
4648
<ProjectProperty Include="PackageVersion" />
4749
</ItemGroup>
4850

51+
<Target Name="UpdatePackagingVersion" BeforeTargets="Pack">
52+
<!-- Update packaging version targets -->
53+
<XmlPoke XmlInputPath="$(OutputPath)SmallSharp.Version.props"
54+
Query="/Project/PropertyGroup/SmallSharpVersion"
55+
Value="$(Version)"/>
56+
</Target>
57+
4958
</Project>

src/SmallSharp/SmallSharp.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@
1010
<UsingSmallSharpSDK Condition="'$(UsingSmallSharpSDK)' == ''">false</UsingSmallSharpSDK>
1111
</PropertyGroup>
1212

13+
<Import Project="SmallSharp.Version.props"/>
14+
1315
</Project>

src/SmallSharp/SmallSharp.targets

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@
3737
<Target Name="StartupFile" BeforeTargets="ResolvePackageAssets;CompileDesignTime;CollectUpToDateCheckInputDesignTime" DependsOnTargets="$(StartupFileDependsOn)" />
3838

3939
<Target Name="EnsureProperties" Condition="'$(CheckSmallSharpRequirements)' != 'false'">
40-
<Error Code="SCS01" Condition="'$(_ImportProjectExtensionProps)' != 'true' or '$(_ImportProjectExtensionTargets)' != 'true'"
40+
<Error Code="SCS02" Condition="'$(_ImportProjectExtensionProps)' != 'true' or '$(_ImportProjectExtensionTargets)' != 'true'"
4141
Text="Setting ImportProjectExtensionProps and ImportProjectExtensionTargets project properties to 'true' is required by SmallSharp to support C# package and project directives." />
42-
<Error Code="SCS02" Condition="'$(ManagePackageVersionsCentrally)' == 'true'"
42+
<Error Code="SCS03" Condition="'$(ManagePackageVersionsCentrally)' == 'true'"
4343
Text="Setting ManagePackageVersionsCentrally to 'true' is not supported by SmallSharp since C# program files can declare package references via #:package directives." />
44+
<Warning Code="SCS04" Condition="'$(UsingSmallSharpSDK)' != 'true'"
45+
Text='For maximum compatibility with file-based apps, use SmallSharp as an SDK instead of a package reference: &lt;Project Sdk="SmallSharp/$(SmallSharpVersion)"&gt;' />
4446
</Target>
4547

4648
<Target Name="CollectStartupFile">
@@ -169,35 +171,13 @@
169171
<EmitTargets StartupFile="$(StartupFile)"
170172
PropsFile="$(SmallSharpPackagesProps)"
171173
TargetsFile="$(SmallSharpPackagesTargets)"
172-
UsingSmallSharpSDK="$(UsingSmallSharpSDK)"
173174
BaseIntermediateOutputPath="$(BaseIntermediateOutputPath)">
174175
<Output TaskParameter="Packages" ItemName="FileBasedPackage" />
175176
<Output TaskParameter="Properties" PropertyName="FileBasedProperty" />
176177
<Output TaskParameter="Sdks" PropertyName="FileBasedSdk" />
177-
<Output TaskParameter="Success" PropertyName="RestoreNeeded" />
178178
</EmitTargets>
179-
</Target>
180-
181-
<Target Name="RestoreBeforeBuild" BeforeTargets="ResolvePackageAssets" DependsOnTargets="EmitTargets"
182-
Condition="'$(RestoreNeeded)' == 'true' and '$(DesignTimeBuild)' != 'true' and '$(UsingSmallSharpSDK)' != 'true'">
183-
184-
<PropertyGroup>
185-
<DynamicProjectAssetsFile>$(MSBuildProjectExtensionsPath)smallsharp.assets.json</DynamicProjectAssetsFile>
186-
</PropertyGroup>
187-
188-
<MSBuild
189-
Projects="$(MSBuildProjectFullPath)"
190-
Targets="Restore"
191-
BuildInParallel="false"
192-
Properties="RestoreUseStaticGraphEvaluation=false;ImportProjectExtensionProps=true;ImportProjectExtensionTargets=true;Guid=$([System.Guid]::NewGuid().ToString())">
193-
</MSBuild>
194-
195-
<Copy SourceFiles="$(ProjectAssetsFile)" DestinationFiles="$(DynamicProjectAssetsFile)" />
196-
197-
<PropertyGroup>
198-
<ProjectAssetsFile>$(DynamicProjectAssetsFile)</ProjectAssetsFile>
199-
</PropertyGroup>
200-
179+
<Error Code="SCS001" Condition="'@(FileBasedSdk)' != '' and '$(UsingSmallSharpSDK)' != 'true'"
180+
Text="Using #:sdk directives requires using SmallSharp as an SDK instead of a package reference: &lt;Project Sdk='SmallSharp/$(SmallSharpVersion)'&gt;" />
201181
</Target>
202182

203183
<UsingTask TaskName="SortItems" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">

0 commit comments

Comments
 (0)