Msbuild: Unnecessary warning "Sdk.props/Sdk.targets cannot be imported again"

Created on 5 Jul 2018  ·  3Comments  ·  Source: dotnet/msbuild

Steps to reproduce

Open the attached project and try to build:
TestProj.zip

I couldn't find any way to get rid of the 2 warnings and still have my project imported.

Expected behavior

No warning when having Sdk="Microsoft.NET.Sdk" in a parent and an imported project
OR
Imported project could in some way inherit the Sdk.

Actual behavior

Showing warning:
1>C:DevTestProjTest.targets : warning MSB4011: "C:Program Filesdotnetsdk2.1.301SdksMicrosoft.NET.SdkSdkSdk.props" cannot be imported again. It was already imported at "C:DevTestProjTestProj.csproj". This is most likely a build authoring error. This subsequent import will be ignored.
1>C:DevTestProjTestProj.csproj : warning MSB4011: "C:Program Filesdotnetsdk2.1.301SdksMicrosoft.NET.SdkSdkSdk.targets" cannot be imported again. It was already imported at "C:DevTestProjTest.targets". This is most likely a build authoring error. This subsequent import will be ignored.

If I remove the Sdk="Microsoft.NET.Sdk" in Test.targets, the target won't run (can be seen from the Output, that will not show "Running...").

Environment data

msbuild /version: 15.7.179.6572
.NET Sdk 2.1.300 or 2.1.301

OS info:
Windows 10 Enterprise
Version 10.0.16299 Build 16299

Visual Studio Enterprise 2017
Version 15.7.4

Most helpful comment

Ah, I see. The problem is that you're hooking TargetTest into the build by overriding a target that is defined in Microsoft.Common.CurrentVersion.targets, which is imported (implicitly by <Project Sdk="Microsoft.NET.Sdk">) _last_, after the full contents of the file.

In this case that means that the default definition of an empty AfterBuild target with no dependencies overrides the one you define in your project (because the last definition of a target wins).

This works with the bad double-SDK-import structure, because the target gets defined in the "inner" import of common.targets and then overridden by the one in the project file.

The import structure becomes:

TestProj.csproj
+-Microsoft.NET.Sdk .props
+-Test.targets
| +-(Microsoft.NET.Sdk .props) # Elided as duplicate, emits MSB4011
| +-Microsoft.NET.Sdk .targets
|   +-Microsoft.Common.CurrentVersion.targets
|     +-AfterBuild (default)
+-AfterBuild (override)
+-(Microsoft.NET.Sdk .targets) # Elided as duplicate, emits MSB4011

There are several ways to fix this:

  1. Hook into the build process in the imported file
diff --git a/Test.targets b/Test.targets
index 2b3702f..8e892a8 100644
--- a/Test.targets
+++ b/Test.targets
@@ -1,5 +1,5 @@
-<Project Sdk="Microsoft.NET.Sdk">
-   <Target Name="TargetTest">
+<Project>
+   <Target Name="TargetTest" BeforeTargets="AfterBuild">
        <Message Text="Running..." Importance="high" />
    </Target>
 </Project>
\ No newline at end of file
diff --git a/TestProj.csproj b/TestProj.csproj
index f55c124..c7edd1c 100644
--- a/TestProj.csproj
+++ b/TestProj.csproj
@@ -5,5 +5,4 @@
   </PropertyGroup>

    <Import Project="Test.targets" />
-   <Target Name="AfterBuild" DependsOnTargets="TargetTest" />
 </Project>
  1. Hook into the build without overriding a target
diff --git a/Test.targets b/Test.targets
index 2b3702f..dc39c96 100644
--- a/Test.targets
+++ b/Test.targets
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project>
    <Target Name="TargetTest">
        <Message Text="Running..." Importance="high" />
    </Target>
diff --git a/TestProj.csproj b/TestProj.csproj
index f55c124..4be9202 100644
--- a/TestProj.csproj
+++ b/TestProj.csproj
@@ -5,5 +5,5 @@
   </PropertyGroup>

    <Import Project="Test.targets" />
-   <Target Name="AfterBuild" DependsOnTargets="TargetTest" />
+   <Target Name="InsertTest" BeforeTargets="AfterBuild" DependsOnTargets="TargetTest" />
 </Project>
  1. Explicitly control the location of the SDK .targets import, so you can put the override after it
diff --git a/Test.targets b/Test.targets
index 2b3702f..dc39c96 100644
--- a/Test.targets
+++ b/Test.targets
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project>
    <Target Name="TargetTest">
        <Message Text="Running..." Importance="high" />
    </Target>
diff --git a/TestProj.csproj b/TestProj.csproj
index f55c124..538fd84 100644
--- a/TestProj.csproj
+++ b/TestProj.csproj
@@ -1,9 +1,12 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project>
+  <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" />

   <PropertyGroup>
     <TargetFramework>net461</TargetFramework>
   </PropertyGroup>

    <Import Project="Test.targets" />
+
+   <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" />
    <Target Name="AfterBuild" DependsOnTargets="TargetTest" />
 </Project>

All 3 comments

There is no need to specify an SDK at the project-element level in a .props or .targets file. The Sdk="Microsoft.NET.Sdk" attribute is a shorthand way of specifying an import at the top and bottom of the project file, which used to be explicit <Import elements.

For the example project here, just make this change:

diff --git a/Test.targets b/Test.targets
index 2b3702f..dc39c96 100644
--- a/Test.targets
+++ b/Test.targets
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project>
    <Target Name="TargetTest">
        <Message Text="Running..." Importance="high" />
    </Target>

The warning is correct--the (simplified) import structure was:

TestProj.csproj
+-Microsoft.NET.Sdk .props
+-Test.targets
| +-Microsoft.NET.Sdk .props
| +-Microsoft.NET.Sdk .targets
+-Microsoft.NET.Sdk .targets

And that can cause real problems in your build, by bypassing default calculations and redefining already-defined properties and items.

@rainersigwald
Unfortunately the answer doesn't satisfy the issue reported.

As I mentioned:
"If I remove the Sdk="Microsoft.NET.Sdk" in Test.targets, the target won't run (can be seen from the Output, that will not show "Running...")."

Ah, I see. The problem is that you're hooking TargetTest into the build by overriding a target that is defined in Microsoft.Common.CurrentVersion.targets, which is imported (implicitly by <Project Sdk="Microsoft.NET.Sdk">) _last_, after the full contents of the file.

In this case that means that the default definition of an empty AfterBuild target with no dependencies overrides the one you define in your project (because the last definition of a target wins).

This works with the bad double-SDK-import structure, because the target gets defined in the "inner" import of common.targets and then overridden by the one in the project file.

The import structure becomes:

TestProj.csproj
+-Microsoft.NET.Sdk .props
+-Test.targets
| +-(Microsoft.NET.Sdk .props) # Elided as duplicate, emits MSB4011
| +-Microsoft.NET.Sdk .targets
|   +-Microsoft.Common.CurrentVersion.targets
|     +-AfterBuild (default)
+-AfterBuild (override)
+-(Microsoft.NET.Sdk .targets) # Elided as duplicate, emits MSB4011

There are several ways to fix this:

  1. Hook into the build process in the imported file
diff --git a/Test.targets b/Test.targets
index 2b3702f..8e892a8 100644
--- a/Test.targets
+++ b/Test.targets
@@ -1,5 +1,5 @@
-<Project Sdk="Microsoft.NET.Sdk">
-   <Target Name="TargetTest">
+<Project>
+   <Target Name="TargetTest" BeforeTargets="AfterBuild">
        <Message Text="Running..." Importance="high" />
    </Target>
 </Project>
\ No newline at end of file
diff --git a/TestProj.csproj b/TestProj.csproj
index f55c124..c7edd1c 100644
--- a/TestProj.csproj
+++ b/TestProj.csproj
@@ -5,5 +5,4 @@
   </PropertyGroup>

    <Import Project="Test.targets" />
-   <Target Name="AfterBuild" DependsOnTargets="TargetTest" />
 </Project>
  1. Hook into the build without overriding a target
diff --git a/Test.targets b/Test.targets
index 2b3702f..dc39c96 100644
--- a/Test.targets
+++ b/Test.targets
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project>
    <Target Name="TargetTest">
        <Message Text="Running..." Importance="high" />
    </Target>
diff --git a/TestProj.csproj b/TestProj.csproj
index f55c124..4be9202 100644
--- a/TestProj.csproj
+++ b/TestProj.csproj
@@ -5,5 +5,5 @@
   </PropertyGroup>

    <Import Project="Test.targets" />
-   <Target Name="AfterBuild" DependsOnTargets="TargetTest" />
+   <Target Name="InsertTest" BeforeTargets="AfterBuild" DependsOnTargets="TargetTest" />
 </Project>
  1. Explicitly control the location of the SDK .targets import, so you can put the override after it
diff --git a/Test.targets b/Test.targets
index 2b3702f..dc39c96 100644
--- a/Test.targets
+++ b/Test.targets
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project>
    <Target Name="TargetTest">
        <Message Text="Running..." Importance="high" />
    </Target>
diff --git a/TestProj.csproj b/TestProj.csproj
index f55c124..538fd84 100644
--- a/TestProj.csproj
+++ b/TestProj.csproj
@@ -1,9 +1,12 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project>
+  <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" />

   <PropertyGroup>
     <TargetFramework>net461</TargetFramework>
   </PropertyGroup>

    <Import Project="Test.targets" />
+
+   <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" />
    <Target Name="AfterBuild" DependsOnTargets="TargetTest" />
 </Project>
Was this page helpful?
0 / 5 - 0 ratings