Skip to content

pwiz: Fixed four vendor readers failing from an installer-based install - #4640

Open
chambm wants to merge 5 commits into
Skyline/work/20260612_net8_portfrom
Skyline/work/20260904_vendor_resolution_fixes
Open

pwiz: Fixed four vendor readers failing from an installer-based install#4640
chambm wants to merge 5 commits into
Skyline/work/20260612_net8_portfrom
Skyline/work/20260904_vendor_resolution_fixes

Conversation

@chambm

@chambm chambm commented Sep 4, 2026

Copy link
Copy Markdown
Member

Summary

Three related pieces, in the order they were found. #4641 has been merged into this branch, so
this is the single PR on top of #4619.

1. Four vendor readers failed from an installer-based install

All pre-existing. None reproduce in a dev or CI build: those keep the vendor DLLs app-local,
and VendorSdkLoader's resolvers prefer app-local over the cache. All four were reproduced on
native Windows with the stock installer before being fixed.

  • Three wrong pins in vendor-sdk-pins.json.
    • The Bruker archive claimed Agilent's MHDAC prefixes (BaseCommon, BaseDataAccess,
      BaseError, BaseTof) although it ships exactly two DLLs, baf2sql_c and timsdata.
      FindPin returns the first prefix match and Bruker precedes Agilent, so a BaseCommon
      bind resolved to the Bruker archive: "extracted cleanly but contains no BaseCommon.dll".
    • MIDAC. never matched: prefixes are StartsWith against the assembly simple name, and
      the assembly is plain MIDAC. Agilent then failed one assembly later on "Could not load
      MIDAC".
    • MobilionShim is pwiz-sharp's own shim, not something the Mobilion archive carries, so
      listing it made the staging filter strip a binary nothing could supply.
  • MobilionShimNative loads MBI_SDK.dll by full path in a static constructor.
    MobilionShim links MBI_SDK.lib, so it is a static import resolved by the OS loader —
    a path no DllImportResolver sees. The error named the wrapper, not the missing dependency.
  • Wiff2LoadContext resolves the wiff2 SDK from the cache when it is not app-local.
    LoadFromAssemblyPath consults no resolver. Same shape as Bruker's
    CompassXtractActivationContext, which calls EnsureExtracted explicitly for the same reason.
  • Shimadzu.csproj deploys VC140 app-local, as Agilent.csproj does VC120 and
    Bruker.csproj VC90. A missing MFC140 makes the reader return a file with no spectra
    rather than an error
    , so any machine carrying the redistributable hides the dependency.

2. A vendor-bundled installer variant (was #4641)

  • build.ps1 -WithVendorSdks produces ProteoWizard-WithVendorSdks-Setup-<ver>.exe
    (103.8 MB): the bundled-runtime installer plus every Windows vendor SDK, pre-extracted into
    the layout VendorSdkLoader.EnsureExtracted would have produced, .ok markers included, so
    the loader neither downloads nor extracts.
  • Setup.iss installs that cache to %PROGRAMDATA%\ProteoWizard\vendor and stamps
    vendor-cache-root.txt, which is what redirects the loader off its per-user default.
  • --with-vendor-sdks through build.bat and tcbuild.bat — opt-in, so a developer build
    does not pay the ~30 s / ~26 MB.
  • Installer.Tests extended from 4 fixtures across 3 vendors to 9 across 8, and made to
    accumulate failures rather than abort on the first. This is what found everything in part 1.
  • Renamed ProteoWizard-Sharp to ProteoWizard across the installer.

3. A failed installer build was reported as a successful CI build

Found on this PR's own CI run (build 4164785, agent EC2AMAZ-HH9KVFB):

12:02:18  Remove-Item: build.ps1:244  The directory is not empty ... Waters-.../vendor_api
12:02:19  installer build failed; Installer.Tests will skip          <-- WARNING only
12:09:09  Test ignored: Install_PerUser_...   No ProteoWizard-Setup-*.exe found
12:09:09  Test Run Successful.  Total tests: 2   Skipped: 2      -> build SUCCESS

So the installer never got built, both Installer.Tests went Inconclusive, and the build went
green — with the coverage that gates part 1 never actually running.

  • build.bat: a failed installer/build.ps1 is now fatal. That branch is only reached
    when ISCC is present (a machine without it takes the else), so a failure there is real.
  • build.ps1:244: the vendor_api cleanup retries, and distinguishes what matters. By
    that point everything wanted has been moved out and the directory holds only the x86/mips
    leftovers — for Waters, two empty directories, deepest path 108 chars, no reparse points,
    nothing read-only. Aborting the installer build over that was the wrong trade. It now retries
    5x with backoff; leftover files still throw (the flatten genuinely failed), leftover
    empty directories warn and continue.

The delete failure is agent-specific, not a defect in the flatten: two builds of the same
commit with the same step passed on MacCoss TeamCity Agent 1 and failed only on the EC2
agent, whose volume behaves unlike local NTFS (the same class of difference that makes
std::filesystem::canonical throw there).

Test plan

  • pwiz-sharp/build.bat Release --i-agree-to-the-vendor-licenses --with-vendor-sdks — build succeeded, all three installers produced (78.2 / 21.5 / 103.8 MB), every suite green: Agilent 17, Waters 21, Bruker 15 +1, Thermo 15, Sciex 8, Mobilion 2, Shimadzu 2, UIMF 2, UNIFI 44, BiblioSpec 142, Analysis 177, MsData 85, Common 58, Util 36, plus the rest
  • installer/build.ps1 -SkipBuild -WithVendorSdks twice in a row — the reported failure was a second-run report
  • Cleanup retry branches exercised with blocker processes: transient block recovers (668 ms); a held-open file throws naming the file; permanently-stuck empty directories warn without throwing
  • build.bat control flow verified separately — goto out of two nested parenthesized blocks under delayed expansion skips the guard line and propagates the exit code; CRLF endings intact
  • Installer.Tests per-user fixture — 9 fixtures across 8 vendors from the real installer artifact
  • 43-pair vendor sweep in the wine 11 container with --network none: 43/43 converted, 40/43 count-match (the 3 diffs are ReaderTestConfig::indexRange-truncated references, not regressions)
  • ABI/Sciex offline check in the same image: 4/4 converted, 11 count-matches
  • Install_PerMachine_DeploysAndConvertsVendorFile is skipped locally — needs elevation

Notes for reviewers

Two things found while investigating, deliberately not fixed here:

  • A pre-existing test race. Lockmass_..._Cwt_AppliedAndUnapplied failed once with
    MassLynx combineScan failed (code 5): File Open Error, then passed 3/3 in isolation and in
    a clean full re-run. ATEHLSTLSEK_profile.raw is shared by Analysis.Tests and
    Waters.Tests, Run-Tests-Parallel.ps1 runs suites as concurrent jobs, and the Waters SDK
    writes lmgt.inf inside the .raw. Fixing it means serializing those two suites or giving
    each a private copy of the fixture.
  • Shallow-clone pin versions. CI logs Shallow clone enforced, and every vendor pin
    collapses to the build SHA (b10f12fe7fe4 for all of them) where a full clone gives each
    archive its own. Not a correctness bug — the generated .cs and the cache agree within a
    build — but the cache key churns every commit on CI instead of tracking archive content.

Co-Authored-By: Claude noreply@anthropic.com

@chambm chambm added the pwiz ProteoWizard/msconvert changes - create Skyline/work branch label Sep 4, 2026
chambm added a commit that referenced this pull request Sep 8, 2026
* Skipped non-Windows natives when staging: msconvert's bin publishes for both RIDs, so the payload carried libMassLynxRaw.so, libbaf2sql_c.so and libtimsdata.so - licensed vendor SDKs the staging filter exists to exclude - plus libhdf5's .so/.dylib set and the Linux apphosts, 46 MB of 97 MB
* Stopped Verify-VendorCache throwing on a surviving vendor_api directory; the loader's own flatten deletes it best-effort and keys only off the .ok marker, so asserting it made the tolerant retry unreachable
* Stopped treating leftover files as a failed flatten, since the flatten skips x86/mips on purpose - 18 such files for Shimadzu, 8 for Agilent, 3 for ABI

Found by /code-review max on #4640.

See TODO-20260901_net10_wine_container.md in pwiz-ai/todos

Co-Authored-By: Claude <noreply@anthropic.com>
@chambm
chambm added this pull request to stack #4647 September 9, 2026 17:25
chambm and others added 4 commits September 9, 2026 13:25
* Corrected three vendor-SDK pins: Bruker claimed Agilent's MHDAC prefixes, MIDAC carried a trailing dot that matches nothing, and Mobilion listed pwiz-sharp's own shim
* Loaded MBI_SDK by full path before the first MobilionShim P/Invoke, which imports it statically where no resolver can see it
* Resolved the wiff2 SDK from the vendor cache when it is not app-local, since LoadFromAssemblyPath consults no resolver
* Staged the VC140 runtime app-local for Shimadzu, as Agilent already does for VC120

See TODO-20260901_net10_wine_container.md in pwiz-ai/todos

Co-Authored-By: Claude <noreply@anthropic.com>
* Added build.ps1 -WithVendorSdks: the bundled-runtime installer plus every Windows vendor SDK pre-extracted into VendorSdkLoader's cache, with a ProgramData marker pointing the app at it
* Wired --with-vendor-sdks through build.bat and tcbuild.bat, opt-in so a developer build does not pay for it
* Extended Installer.Tests from 4 fixtures across 3 vendors to 9 across 8, and made it accumulate failures instead of aborting on the first
* Renamed ProteoWizard-Sharp to ProteoWizard across the installer

See TODO-20260901_net10_wine_container.md in pwiz-ai/todos

Co-Authored-By: Claude <noreply@anthropic.com>
…I build

* Made a failed installer/build.ps1 fatal in build.bat; that branch is only reached when ISCC is present, and warning instead let Installer.Tests go Inconclusive and the build go green with the vendor coverage never run
* Retried the vendor_api cleanup, which threw "The directory is not empty" on a TeamCity EC2 agent over a tree holding nothing but empty directories
* Kept a leftover file fatal there, since that means the flatten itself failed

See TODO-20260901_net10_wine_container.md in pwiz-ai/todos

Co-Authored-By: Claude <noreply@anthropic.com>
* Skipped non-Windows natives when staging: msconvert's bin publishes for both RIDs, so the payload carried libMassLynxRaw.so, libbaf2sql_c.so and libtimsdata.so - licensed vendor SDKs the staging filter exists to exclude - plus libhdf5's .so/.dylib set and the Linux apphosts, 46 MB of 97 MB
* Stopped Verify-VendorCache throwing on a surviving vendor_api directory; the loader's own flatten deletes it best-effort and keys only off the .ok marker, so asserting it made the tolerant retry unreachable
* Stopped treating leftover files as a failed flatten, since the flatten skips x86/mips on purpose - 18 such files for Shimadzu, 8 for Agilent, 3 for ABI

Found by /code-review max on #4640.

See TODO-20260901_net10_wine_container.md in pwiz-ai/todos

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 9, 2026 17:25
@chambm
chambm force-pushed the Skyline/work/20260904_vendor_resolution_fixes branch from 13ad422 to f1f64f3 Compare September 9, 2026 17:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Installer.Tests’ new “accumulate failures” logic still aborts on non-assert exceptions, and should be adjusted so it consistently reports all vendor failures as intended.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Fixes multiple vendor-reader failures that only occurred in installer-based installs (where vendor SDK DLLs are resolved from the VendorSdkLoader cache rather than app-local), and improves CI/packaging to reliably build and validate installer artifacts (including an offline-friendly vendor-bundled installer variant).

Changes:

  • Correct vendor SDK pin prefixes and adjust vendor SDK extraction/resolution so installed builds reliably load Sciex wiff2 and Mobilion dependencies from the cache.
  • Add support for an optional “WithVendorSdks” installer variant that pre-populates the shared vendor cache and stamps a cache-root override for offline/container installs.
  • Make installer build failures fatal in CI and improve installer payload staging/cleanup behavior.
File summaries
File Description
pwiz-sharp/tcbuild.bat Forces CI builds to include --with-vendor-sdks when invoking build.bat.
pwiz-sharp/pwiz/test/Installer.Tests/InstallerTests.cs Expands installer smoke coverage to more vendors and reports multiple vendor failures in one run.
pwiz-sharp/pwiz/src/Vendor/Shimadzu/Shimadzu.csproj Deploys VC140 runtime DLLs app-local for Shimadzu native dependencies.
pwiz-sharp/pwiz/src/Vendor/Sciex/Wiff2LoadContext.cs Resolves Sciex SDK directory from app-local or VendorSdkLoader cache for installed builds.
pwiz-sharp/pwiz/src/Vendor/Sciex/Sciex.csproj Adds a direct reference to Vendor.Common to support explicit cache extraction for wiff2 SDK loading.
pwiz-sharp/pwiz/src/Vendor/Mobilion/MobilionShimNative.cs Ensures MBI_SDK.dll is loaded by full path from the vendor cache before P/Invoke loads MobilionShim.dll.
pwiz-sharp/pwiz/src/Vendor/Mobilion/Mobilion.csproj Adds a direct reference to Vendor.Common to support explicit cache extraction for Mobilion.
pwiz-sharp/pwiz/src/Vendor/Bruker/Bruker.csproj Updates comment describing Vendor.Common direct usage (now shared with Sciex).
pwiz-sharp/installer/Setup.iss Renames product to ProteoWizard and adds an optional vendor-cache install + cache-root stamping for offline installs.
pwiz-sharp/installer/build.ps1 Adds -WithVendorSdks pipeline, improves staging filters (non-Windows payload trimming), and stages msconvert/wiff2 outputs explicitly.
pwiz-sharp/installer/build-linux.sh Renames Linux artifact names to match ProteoWizard naming.
pwiz-sharp/build/vendor-sdk-pins.json Fixes incorrect prefixes (Bruker/Agilent/Mobilion) that broke SDK resolution from the cache.
pwiz-sharp/build.bat Adds --with-vendor-sdks and makes installer build failures fatal to prevent false-green CI runs.
Review details
  • Files reviewed: 7/13 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +439 to +442
catch (AssertFailedException ex)
{
failures.Add($"{vendor}/{name}: {ex.Message}");
}
Comment on lines +164 to +166
; uninsneveruninstall because the cache is keyed by SDK version, not by app
; version, so side-by-side pwiz-sharp installs share these directories — the
; same last-installed-wins / leave-it-alone policy the Explorer verbs use.
…n machine

* Staged the app-local VC++ runtime into the vendor cache at extraction time: a cache DLL is loaded by full path, which drops the application directory from the native search order, so BaseTof.dll could not find the MSVCR120 it imports
* Applied it to every vendor, and matched the build-time bundled cache to the same set, so an offline installer cannot disagree with a downloaded one
* Cost 0.6 MB on the installer; the eight identical copies compress away

Found by the extended Installer.Tests on a clean CI agent.

See TODO-20260901_net10_wine_container.md in pwiz-ai/todos

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pwiz ProteoWizard/msconvert changes - create Skyline/work branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants