Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ updates:
- "*"

- package-ecosystem: "nuget"
directory: "/NtoLib"
# Repo-root scope: with Central Package Management enabled, all versions
# live in Directory.Packages.props at the repo root. Pointing dependabot
# at "/" lets it discover both NtoLib.csproj and Tests/Tests.csproj and
# update the central versions file in a single PR per group.
directory: "/"
schedule:
interval: "monthly"
groups:
Expand Down
12 changes: 9 additions & 3 deletions .github/instructions/csharp.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ COM component via `netreg.exe`.

- **Framework:** .NET Framework 4.8, C# 10 (`LangVersion`), `Nullable` enabled
- **Solution:** `NtoLib.sln` — two projects: `NtoLib` (main library) and `Tests` (xUnit)
- **csproj style:** Old-style with explicit `<Compile Include>` entries (no wildcards)
- **Build:** `dotnet build` → ILRepack merges NuGet DLLs into `NtoLib.dll` (vendor SDK DLLs
in `Resources/` are excluded); `netreg.exe` registers the COM component for MasterSCADA
- **csproj style:** SDK-style (`Microsoft.NET.Sdk`) with `EnableDefaultCompileItems=false`;
every `.cs` file is listed explicitly as `<Compile Include>` (no wildcards). Packages
are referenced via `<PackageReference>`; versions live centrally in
`Directory.Packages.props` (Central Package Management).
- **Build:** `dotnet build` produces an un-merged `NtoLib.dll` for unit tests. Merging
is gated on `/p:RunILRepack=true` (because `[InternalsVisibleTo("Tests")]` would
otherwise cause CS0433 ambiguity in the test project) and runs only via
`Build/Package.ps1` / `Build/deploy.ps1` after the test step. `netreg.exe` registers
the merged `NtoLib.dll` as a COM component for MasterSCADA.

---

Expand Down
16 changes: 5 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ on:
- "Tests/**"
- "Resources/**"
- "NtoLib.sln"
- "Directory.Packages.props"
- "nuget.config"
- "global.json"
- ".editorconfig"
- "!**.md"
Expand All @@ -19,6 +21,8 @@ on:
- "Tests/**"
- "Resources/**"
- "NtoLib.sln"
- "Directory.Packages.props"
- "nuget.config"
- "global.json"
- ".editorconfig"
- "!**.md"
Expand All @@ -45,18 +49,8 @@ jobs:
with:
global-json-file: global.json

- name: setup NuGet
uses: NuGet/setup-nuget@v2

# NtoLib.csproj uses packages.config with MSBuild <Import> of
# ..\packages\Serilog.4.3.0\build\Serilog.targets — dotnet restore does
# not populate packages/ for old-style projects, so nuget.exe restore
# runs first. dotnet restore then covers the Tests project's
# PackageReference graph.
- name: restore
run: |
nuget restore NtoLib.sln
dotnet restore NtoLib.sln
run: dotnet restore NtoLib.sln

- name: build
run: dotnet build NtoLib.sln -c Release --no-restore
Expand Down
68 changes: 59 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,37 @@ via `netreg.exe`.

- **Framework:** .NET Framework 4.8, C# 10, Nullable enabled
- **Solution:** `NtoLib.sln` — two projects: `NtoLib` (main library) and `Tests` (xUnit)
- **csproj style:** Old-style with explicit `<Compile Include>` entries (no wildcards)
- **csproj style:** SDK-style (`<Project Sdk="Microsoft.NET.Sdk">`) with explicit
`<Compile Include>` entries — default item globs are disabled
(`EnableDefaultCompileItems=false`, `EnableDefaultEmbeddedResourceItems=false`,
`EnableDefaultNoneItems=false`) so the "no wildcards" rule still holds.
- **NuGet:** `PackageReference` only; versions centrally managed in
`Directory.Packages.props` at the repo root (Central Package Management).

## Build

```powershell
dotnet build NtoLib.sln
Build/Package.ps1 # build + ILRepack merge + archive
Build/Deploy.ps1 # build + merge + copy to target machine
dotnet build NtoLib.sln # un-merged DLL (used by tests)
dotnet build NtoLib.sln -p:RunILRepack=true # merged DLL (used for deployment)
Build/Package.ps1 # build + test + ILRepack merge + archive
Build/deploy.ps1 # build + merge + copy to target machine
```

`Build/Package.ps1` orchestrates the pipeline: it runs the test suite against the
un-merged DLL, then re-builds with `-p:RunILRepack=true` to produce the merged
artifact, then archives.

ILRepack merges all NuGet DLLs into a single `NtoLib.dll`, excluding vendor SDK DLLs
(`FB.dll`, `InSAT.*`, `MasterSCADA.*`). After build, `netreg.exe NtoLib.dll /showerror`
registers the COM component for MasterSCADA.
(`FB.dll`, `InSAT.*`, `MasterSCADA.*`). The merge step is implemented in
`NtoLib/ILRepack.targets` (invoked through MSBuild via the
`ILRepack.Lib.MSBuild.Task` package); the legacy `Build/tools/Merge.ps1` PowerShell
wrapper has been removed. The target is gated on the `RunILRepack` MSBuild
property so a plain `dotnet build` produces an un-merged DLL — this is intentional
and required, because `NtoLib` carries `[InternalsVisibleTo("Tests")]` and merging
internalised NuGet types into the assembly would collide with the Tests project's
own `<PackageReference>`s on the same packages (CS0433 ambiguity on
`FluentResults.Result<>`, `Microsoft.Extensions.*`, etc.). After the merged build,
`netreg.exe NtoLib.dll /showerror` registers the COM component for MasterSCADA.

## Test

Expand Down Expand Up @@ -46,7 +64,7 @@ Code Cleanup or `jb cleanupcode`.
NtoLib/
├── NtoLib/ main library (FB implementations)
├── Tests/ xUnit + FluentAssertions + Moq
├── Build/ PowerShell pipeline (Package.ps1, Deploy.ps1, tools/)
├── Build/ PowerShell pipeline (Package.ps1, deploy.ps1, tools/)
├── Docs/ documentation
│ ├── architecture/ primer + NtoLib patterns (LLM-targeted, not user-facing)
│ ├── known_issues/ platform-level bug classes with cause and workaround
Expand All @@ -72,14 +90,46 @@ for the reading order).

## csproj Conventions

- `NtoLib.csproj` uses **explicit `<Compile Include>` entries** — no wildcards. Every new
`.cs` file must be added manually.
- `NtoLib.csproj` is **SDK-style** (`<Project Sdk="Microsoft.NET.Sdk">`) but uses
**explicit `<Compile Include>` entries** — no wildcards. Default item globs are
disabled via `EnableDefaultCompileItems=false`,
`EnableDefaultEmbeddedResourceItems=false`, `EnableDefaultNoneItems=false`, and
`GenerateAssemblyInfo=false`. Every new `.cs` file must be added manually.
- FB XML pin configuration files must be included as **`<EmbeddedResource>`**, not `<Content>`:
```xml
<EmbeddedResource Include="MyFeature\MyFB.xml" />
```
- When adding a new FB with multiple source files, add all `<Compile Include>` and the
`<EmbeddedResource>` entry in a single csproj edit to avoid partial builds.
- `packages.config` is **gone** — the project uses `<PackageReference>` exclusively.

### Central Package Management

- NuGet package versions are centrally managed in `Directory.Packages.props` at the
repo root (`ManagePackageVersionsCentrally=true`,
`CentralPackageTransitivePinningEnabled=true`).
- Add a new package by appending a `<PackageVersion Include="..." Version="..." />`
entry to `Directory.Packages.props`, then reference it from the consuming csproj
with `<PackageReference Include="..." />` — **no `Version=` attribute on the
`PackageReference`**. NuGet emits `NU1008` (hard error) if a `Version=` attribute
is left in place under CPM, and `NU1010` if a referenced id has no
`<PackageVersion>` entry.
- Dependabot is configured against the repo root and bumps versions in
`Directory.Packages.props` only; csproj files are not touched by version updates.

### ILRepack and `[InternalsVisibleTo("Tests")]`

- The merge target lives in `NtoLib/ILRepack.targets` and is wired in via the
`ILRepack.Lib.MSBuild.Task` package's `$(ILRepackTargetsFile)` hook.
- The target only runs when the `RunILRepack` MSBuild property is `true`. A plain
`dotnet build` therefore produces an **un-merged** DLL — this is required for the
Tests project to build, because merging internalises NuGet types that Tests also
references directly.
- `Build/Package.ps1` runs the test suite against the un-merged DLL first, then
re-invokes `dotnet build NtoLib/NtoLib.csproj -c Release -p:RunILRepack=true` to
produce the deployable merged artifact between the Test and Archive steps.
- `Build/tools/Merge.ps1` no longer exists; do not re-introduce a PowerShell merge
wrapper.

## Dependencies

Expand Down
11 changes: 10 additions & 1 deletion Build/Package.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,14 @@ if (-not (Test-Path $RepoRoot))

& (Join-Path $ToolsDir 'Build.ps1') -Configuration $Configuration -RepoRoot $RepoRoot
& (Join-Path $ToolsDir 'Test.ps1') -Configuration $Configuration -RepoRoot $RepoRoot
& (Join-Path $ToolsDir 'Merge.ps1') -Configuration $Configuration -RepoRoot $RepoRoot

# Bundle NuGet runtime dependencies into NtoLib.dll via the ILRepack.targets MSBuild target.
# Gated by /p:RunILRepack=true so the preceding test build stays un-merged
# (see NtoLib/ILRepack.targets header for the rationale).
dotnet build (Join-Path $RepoRoot 'NtoLib\NtoLib.csproj') -c $Configuration -v minimal -p:RunILRepack=true --no-restore
if ($LASTEXITCODE -ne 0)
{
throw "ILRepack merge step failed with exit code $LASTEXITCODE."
}

& (Join-Path $ToolsDir 'Archive.ps1') -Configuration $Configuration -RepoRoot $RepoRoot
11 changes: 10 additions & 1 deletion Build/deploy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,14 @@ if ( [string]::IsNullOrWhiteSpace($ConfigurationDirectory))
}

& (Join-Path $ToolsDir 'Build.ps1') -Configuration $Configuration -RepoRoot $RepoRoot
& (Join-Path $ToolsDir 'Merge.ps1') -Configuration $Configuration -RepoRoot $RepoRoot

# Bundle NuGet runtime dependencies into NtoLib.dll via the ILRepack.targets MSBuild target.
# Gated by /p:RunILRepack=true so the preceding solution build stays un-merged
# (see NtoLib/ILRepack.targets header for the rationale).
dotnet build (Join-Path $RepoRoot 'NtoLib\NtoLib.csproj') -c $Configuration -v minimal -p:RunILRepack=true --no-restore
if ($LASTEXITCODE -ne 0)
{
throw "ILRepack merge step failed with exit code $LASTEXITCODE."
}

& (Join-Path $ToolsDir 'Copy.ps1') -Configuration $Configuration -RepoRoot $RepoRoot -DestinationDirectory $DestinationDirectory -ConfigurationDirectory $ConfigurationDirectory
19 changes: 7 additions & 12 deletions Build/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,18 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\Build\tools\Build.ps1

### 2) Слияние зависимостей (Merge)

Скрипт: `Build\tools\Merge.ps1`
Назначение: объединение `NtoLib.dll` и большинства managed-зависимостей в один файл с помощью **ILRepack**.
Слияние выполняется через MSBuild-таргет `NtoLib/ILRepack.targets`, использующий пакет **ILRepack.Lib.MSBuild.Task**. Отдельного скрипта `Merge.ps1` больше нет — таргет вызывается напрямую через `dotnet build` с флагом `-p:RunILRepack=true`.

Скрипт:
- берёт все `*.dll` из `NtoLib\bin\<BUILD_CONFIGURATION>\`
- объединяет их в `NtoLib.dll`
Поведение таргета:
- объединяет managed-зависимости из `NtoLib\bin\<BUILD_CONFIGURATION>\` в `NtoLib.dll`
- **не включает** хостовые зависимости MasterSCADA (например `FB.dll`, `MasterSCADA.*`) и **не включает** `System.Resources.Extensions.dll`
- использует `/lib:` для каталогов поиска зависимостей при анализе ссылок
- активируется только при `RunILRepack=true`, чтобы юнит-тесты собирались с не-смерженной сборкой

Запуск:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\Build\tools\Merge.ps1
dotnet build .\NtoLib\NtoLib.csproj -c Release -p:RunILRepack=true
```

Требование: установлен ILRepack в каталоге:
- `NtoLib\packages\ILRepack.2.0.44\tools\ILRepack.exe`

### 3) Тесты (Test)

Скрипт: `Build\tools\Test.ps1`
Expand Down Expand Up @@ -158,9 +153,9 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\Build\Package.ps1

## Прямые команды dotnet (при необходимости)

Restore (packages.config):
Restore (PackageReference + Central Package Management через `Directory.Packages.props` в корне репозитория):
```powershell
dotnet msbuild .\NtoLib.sln /t:Restore /p:RestorePackagesConfig=true
dotnet restore .\NtoLib.sln
```

Build:
Expand Down
Loading