Skip to content

[BUG] Fortran source files don't propagate through library-manifest dependency resolution outside task: 'build' #11930

@batpigandme

Description

@batpigandme

Description

Error is encountered when a Fortran benchmark for a Level-2+ BLAS routine whose Fortran source declares an EXTERNAL Fortran dependency on another stdlib package (e.g., dgemm calling xerbla, dger calling xerbla). The benchmark fails to link with a missing-symbol error for the dependency.

The underlying defect is in @stdlib/utils/library-manifest. When the resolver is queried with any task other than build (notably task: 'benchmark'), Fortran source files belonging to dependencies are not surfaced.

Fortran sources are currently declared only inside the build task entry of each Fortran-using package's manifest — so when library-manifest walks the dependency tree under task: 'benchmark', it merges each dependency's benchmark entry, which contains only C sources, and the dependency's .f files never appear in the merged result.

For example, in blas/base/xerbla/manifest.json:

  • task: 'build', os: 'linux', blas: '', wasm: falsesrc: ["./src/xerbla.f", "./src/xerbla.c"]
  • task: 'benchmark', os: 'linux', blas: '', wasm: falsesrc: ["./src/xerbla.c"] (no .f)

The native-addon build path works bc binding.gyp/include.gypi calls library-manifest with no explicit task, defaulting to task: 'build', where the .f declarations exist.

The same condition silently affects every merged Fortran benchmark for a Level-2+ BLAS routine that exercises a cross-package Fortran symbol. Level-1 BLAS Fortran benchmarks (e.g., daxpy, dscal, dasum) have no cross-package Fortran dependencies and link cleanly, which is why the gap has not surfaced more broadly.

The recurring local fix is a Makefile patch in the consuming package that hardcodes a relative path to a sibling package's .f file (e.g., ../../../xerbla/src/xerbla.f inside dger's benchmark Makefile). This couples one package to another package's file-tree layout. If xerbla is ever moved within the package tree (e.g., to @stdlib/blas/base/utils/xerbla), every consumer's hardcoded path silently breaks, and a contributor performing the migration has no way to discover the breakage short of running every affected benchmark. The manifest-based @stdlib/... reference model exists precisely so packages don't reach into each other's file trees this way. See @kgryte's comment on PR #11332.

A second, downstream contributor to the symptom is that tools/scripts/compile_fortran_benchmark only resolves include directories from the manifest, while its C counterpart tools/scripts/compile_c_benchmark resolves include, src, libraries, and libpath. Patching only this wrapper might be too narrow, since it wouldn't address future Fortran-linking contexts that go through library-manifest.

Per office hours discussion, the fix should live in @stdlib/utils/library-manifest so that Fortran source files are resolved uniformly when resolving a package's dependency tree, regardless of the task the caller queried.

Once the resolver is correct, downstream cleanup for the Fortran benchmark wrapper in tools/scripts/ should consume the resolved source list (mirroring its C counterpart), and the merged Fortran benchmark Makefiles' hardcoded SOURCE_FILES defaults should match the C-benchmark Makefile convention.

Related Issues

Related PRs #11332 and #11333.

Questions

No.

Demo

No response

Reproduction

# In a checkout of stdlib develop:
cd lib/node_modules/@stdlib/blas/base/dger/benchmark/fortran
make

Expected Results

Benchmark links successfully; xerbla resolves via the manifest dependency graph (dger's manifest already lists @stdlib/blas/base/xerbla as a dependency under task: 'benchmark'.

Actual Results

Undefined symbols for architecture arm64:
  "_xerbla", referenced from:
      _dger in ccCfvnCt.o
ld: symbol(s) not found for architecture arm64
collect2: error: ld returned 1 exit status
make: *** [benchmark.length.out] Error 1

Linker fails with an unresolved reference to xerbla. The dependency's .f source is never surfaced because library-manifest only declares Fortran sources under task: 'build'.

Version

develop

Environments

N/A

Browser Version

No response

Node.js / npm Version

No response

Platform

Reproduces on any platform with gfortran + make (linux/macOS verified).

Checklist

  • Read and understood the Code of Conduct.
  • Searched for existing issues and pull requests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions