Repository navigation
Expand file tree
/
Copy pathDirectory.Build.props
More file actions
190 lines (164 loc) · 13.7 KB
/
Copy pathDirectory.Build.props
File metadata and controls
190 lines (164 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
<Project>
<!-- ====================================================================================
Strong-name signing — ONE place for every project in the repository.
WHAT THE KEY IS. `Open.snk` at the repo root is Microsoft's *published* open-source
strong-name key, byte-for-byte identical (596 bytes, md5 2fe5bdce4ef988fa5bd982debb350668)
to dotnet/arcade's `src/Microsoft.DotNet.Arcade.Sdk/tools/snk/Open.snk`. Its public key
token `cc7b13ffcd2ddd51` is the one carried by `netstandard`, `System.Memory`,
`System.Buffers`, `System.Text.Json` and `System.Runtime.CompilerServices.Unsafe` —
Microsoft strong-names those with this very key and publishes the PRIVATE half so OSS
builds can produce strong-named output without its real signing infrastructure.
SO THIS IS IDENTITY, NOT AUTHENTICITY. An .snk is a bare RSA key pair: no certificate,
no subject, no issuer, no chain. It lets the loader tell this `NumSharp` from someone
else's and bind versions; it attests NOTHING about origin, and with a key whose private
half is public, anyone can mint an assembly bearing our identity. That is the key's
stated purpose, not a leak. Real authenticity is Authenticode on the DLLs plus NuGet
author signing, needs a code-signing certificate, and is entirely separate from this file.
=> Do NOT treat `InternalsVisibleTo` below as an access control. It is not one.
WHY THIS KEY AND NOT OUR OWN. TensorFlow.NET signs with the identical key, which is what
makes the cross-repo `InternalsVisibleTo("TensorFlowNET.UnitTest")` in
src/NumSharp.Core/Assembly/Properties.cs resolve. Minting a NumSharp-owned key would
break that friend reference for zero security gain.
HOW IT WAS BROKEN BEFORE. `SignAssembly` used to live in a `Publish|AnyCPU`-only
PropertyGroup in each csproj, and CI builds and packs `Release` — so nothing ever
shipped signed (every published NumSharp DLL reads `PublicKeyToken=null`). Worse, that
`Publish` configuration did not even COMPILE: strong-naming makes a keyless
`InternalsVisibleTo` a hard `CS1726`, and all five of ours were keyless. A dead
`#if !SIGNING` guard was meant to cover this, but `SIGNING` was defined only in the
*test* csproj, a different assembly, where it did nothing.
Opt out per project with `<SignAssembly>false</SignAssembly>` — Directory.Build.props is
imported BEFORE the project body, so a project's own value wins.
==================================================================================== -->
<PropertyGroup>
<SignAssembly Condition="'$(SignAssembly)' == ''">true</SignAssembly>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)Open.snk</AssemblyOriginatorKeyFile>
<!-- We hold the private half, so sign for real: not delay-signed (which would leave the
signature blank and require skip-verification registration) and not public-signed
(an OSS-build shortcut that stamps the public key without a valid signature). -->
<DelaySign>false</DelaySign>
<PublicSign>false</PublicSign>
<!-- The public key, for `InternalsVisibleTo(..., PublicKey=...)` and for anything else that
needs to name our identity. Kept here so the literal is written down exactly once; the
token is asserted at test time by StrongNameTests. -->
<NumSharpPublicKey>00240000048000009400000006020000002400005253413100040000010001004b86c4cb78549b34bab61a3b1800e23bfeb5b3ec390074041536a7e3cbd97f5f04cf0f857155a8928eaa29ebfd11cfbbad3ba70efea7bda3226c6a8d370a4cd303f714486b6ebc225985a638471e6ef571cc92a4613c00b8fa65d61ccee0cbe5f36330c9a01f4183559f1bef24cc2917c6d913e3a541333a1d05d9bed22b38cb</NumSharpPublicKey>
<NumSharpPublicKeyToken>cc7b13ffcd2ddd51</NumSharpPublicKeyToken>
</PropertyGroup>
<!-- ====================================================================================
JIT optimization posture — TieredCompilation + TieredPGO, ON for the whole repo.
WHAT THESE ARE. Both are PROCESS-level runtime knobs, not per-assembly switches: they are
written into the ENTRY process's `<app>.runtimeconfig.json` as
`System.Runtime.TieredCompilation` / `System.Runtime.TieredPGO`. Setting them here turns
them on for every project in the repo that PRODUCES a runtimeconfig — the test hosts, the
benchmarks, `tools/`, and the file-based `dotnet run` scripts under the repo root
(Directory.Build.props is an implicit import for those). Those processes then start with
tiered compilation AND profile-guided optimization, so hot methods are recompiled to
PGO-guided tier-1 — the most-optimized code the JIT produces.
WHY BOTH ENABLED, AND NOT `TieredCompilation=false`. TieredPGO REQUIRES tiering: it
instruments the tier-0 code to gather the profile that guides tier-1. So "optimize
aggressively using BOTH" can only mean both switched ON. Disabling tiering would skip
tier-0 entirely — which also drops PGO and slows every first-call/startup — a different and
usually worse steady-state trade, so it is deliberately NOT done globally here.
SCOPE / REAL EFFECT. On the src/ class LIBRARIES (NumSharp.Core, NumSharp.Bitmap,
NumSharp.Interop.*) this is INERT: a library emits no runtimeconfig, and the CONSUMING
app's own runtimeconfig governs the whole process. A downstream consumer that wants this
posture sets the same two properties in THEIR application project. Both values are also
the .NET 8+ defaults, so this pins the intent and guarantees it across SDKs/environments
rather than changing measured behaviour — the only setting that would change behaviour is
turning tiering OFF, which "both" excludes.
A project overrides in its own body (imported AFTER this file, so it wins), and the
`== ''` guards also let a command-line `-p:` / global property win — e.g.
benchmark/NumSharp.Benchmark.Sandbox deliberately sets both false to measure cold tier-1, and
the three CI test hosts (NumSharp.Tests, NumSharp.Tests.Oracle, NumSharp.Tests.Build.Analyzer)
set TieredPGO=false: a short, JIT-bound test run never pays back PGO's instrumented tier
(measured 7-15 % faster; rationale and numbers in test/NumSharp.Tests/NumSharp.Tests.csproj).
==================================================================================== -->
<PropertyGroup>
<TieredCompilation Condition="'$(TieredCompilation)' == ''">true</TieredCompilation>
<TieredPGO Condition="'$(TieredPGO)' == ''">true</TieredPGO>
</PropertyGroup>
<!-- ====================================================================================
Package version — ONE number for every project in the repository.
`VersionPrefix` is the single source of truth. The SDK derives everything else from it
(Microsoft.NET.DefaultAssemblyInfo.targets, Microsoft.NET.GenerateAssemblyInfo.targets,
NuGet.Build.Tasks.Pack.targets), so NO csproj declares Version / PackageVersion /
AssemblyVersion / FileVersion:
Version = VersionPrefix[-VersionSuffix] 0.60.0 or 0.60.0-rc.1
PackageVersion = Version the nupkg version — and the
version a ProjectReference
becomes as a nuspec dependency
AssemblyVersion = Version minus pre-release/metadata 0.60.0.0
FileVersion = AssemblyVersion
InformationalVersion = Version+<SourceRevisionId> the git commit (SourceLink)
WHY. The per-csproj copies had drifted: NumSharp.Bitmap carried a 4-part 0.60.0.0 beside the
others' 0.60.0, and every package hard-coded <PackageVersion> next to a
`$(Version)-$(VersionSuffix)` conditional — so `-p:VersionSuffix=rc.1` produced an rc.1
ASSEMBLY inside a non-rc PACKAGE (an explicit PackageVersion is never re-derived). Deriving
every number from one prefix makes those mismatches unrepresentable and keeps the five
packages — NumSharp, NumSharp.Bitmap, NumSharp.Interop.pythonnet, NumSharp.Interop.OpenBLAS,
NumSharp.Build — co-versioned by construction, nuspec dependency ranges included.
The release pipeline overrides all of it from the git tag with ONE global property,
`-p:Version=<tag>` (a global property beats every conditional here and in the SDK), so this
value is only the local-dev default; bump it when a release is cut. `-p:VersionSuffix=rc.1`
still gives a local pre-release pack, now consistent across assembly and package.
==================================================================================== -->
<PropertyGroup>
<VersionPrefix Condition="'$(VersionPrefix)' == ''">0.80.0</VersionPrefix>
</PropertyGroup>
<!-- ====================================================================================
License acceptance — OFF for every package, in ONE place so they cannot drift apart.
All five packages are Apache-2.0 (the OpenBLAS interop adds the permissive BSD and
GCC-exception terms of the binaries it bundles); a click-through prompt on install buys
nothing for a permissive license and is unusual for OSS on nuget.org.
==================================================================================== -->
<PropertyGroup>
<PackageRequireLicenseAcceptance Condition="'$(PackageRequireLicenseAcceptance)' == ''">false</PackageRequireLicenseAcceptance>
</PropertyGroup>
<!-- ====================================================================================
Shared NuGet package icon — ONE brand asset for every shippable package, so it cannot
drift the way the per-csproj `PackageIconUrl` did. That element is deprecated (NU5048)
and renders NO icon on nuget.org; `PackageIcon` embeds a real image in the package.
Centralised here beside signing because it is identical across NumSharp / NumSharp.Bitmap /
NumSharp.Interop.* — a new package inherits it for free instead of copy-pasting a fourth
icon include. $(MSBuildThisFileDirectory) is the repo root, so the path resolves for a
project in any subdirectory, and the checked-in 128×128 PNG travels to CI with the repo.
The <None> is inert for non-packable projects (tests/benchmarks never pack); only the five
packable projects embed it, and only when packed explicitly with `dotnet pack` (nothing in
the repo packs on build). Override `PackageIcon` in a project to change it.
==================================================================================== -->
<PropertyGroup>
<PackageIcon Condition="'$(PackageIcon)' == ''">numsharp.icon128.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)docs\website-src\images\numsharp.icon128.png"
Pack="true" PackagePath="\" Visible="false" />
</ItemGroup>
<!-- ====================================================================================
No embedded package README — deliberately, for EVERY package (since after 0.70.0).
WHY. nuget.org shows one readme per package version on its README tab. When the .nupkg
embeds one (`PackageReadmeFile` → the nuspec's `<readme>`), the gallery ALWAYS renders
that file and ignores the owner-editable readme on the package's Manage page — and the
embedded copy can never change afterwards, because the package is signed and immutable.
Leaving the readme OUT keeps every version's README tab editable after publishing, which
is where the owner pastes that release's GitHub release markdown by hand:
nuget.org → package → Manage → Readme → select the version → Custom → Submit.
The nuspec's `<releaseNotes>` is no substitute: it is just as immutable after publish and
nuget.org renders it as plain text. 0.70.0 shipped embedded readmes, which is exactly why
its README tab can never carry its release notes (0.40.0–0.60.0 did not, so theirs could).
The project folders' README.md files stay in the repo as GitHub/docs documentation; they
are simply not packed. A package without a readme still shows its `Description` on the
README tab until the owner submits one.
ENFORCED. The target below fails `dotnet pack` for any project that sets
`PackageReadmeFile`, so the policy cannot regress silently through a copied csproj or a
well-meaning "add a readme" fix — a slip that would only surface after an irreversible
publish. It hooks `GenerateNuspec`, which `dotnet pack` runs with or without `no-build`.
It is declared here, ahead of the project body, yet still sees a project's own
`PackageReadmeFile`: a target's Condition is evaluated when the target runs, after the
whole project has been evaluated. To return to embedded readmes on purpose, delete this
block together with the guard.
==================================================================================== -->
<Target Name="NumSharpRejectEmbeddedPackageReadme"
BeforeTargets="GenerateNuspec"
Condition="'$(PackageReadmeFile)' != ''">
<Error Text="$(MSBuildProjectFile) sets PackageReadmeFile='$(PackageReadmeFile)', but NumSharp packages must not embed a README: nuget.org always shows an embedded readme and then ignores the owner-editable one, so this version's README tab could never be given its release notes after publishing. Remove PackageReadmeFile (and the packed README.md item) - see 'No embedded package README' in Directory.Build.props." />
</Target>
</Project>