build method
Future<NativeBuildResult>
build({
- required NativeTarget target,
- required Directory outputDir,
- Directory? workDir,
- LinkMode? linkMode,
Build for a specific target.
outputDir is where artifacts are staged (e.g., built-library/linux-x64/).
workDir is the working directory for build commands.
Implementation
Future<NativeBuildResult> build({
required NativeTarget target,
required Directory outputDir,
Directory? workDir,
LinkMode? linkMode,
}) async {
logger?.info('Building ${project.name} for ${target.label}...');
// 1. Resolve recipe
final recipe = project.build.recipeFor(target);
if (recipe == null) {
logger?.info('No declarative recipe found; invoking hook/build.dart.');
return _buildViaHookCli(
target: target,
outputDir: outputDir,
linkMode: linkMode ?? project.asset.linkMode,
packageRoot: source?.directory ?? Directory.current,
);
}
// 2. Resolve source if not provided
var resolvedSource = source;
if (resolvedSource == null) {
// When no explicit source or sourceFallback is given, fall back
// to project.sources so that CLI callers (and any executor user)
// can build from the project's declared sources automatically.
final effectiveFallback =
sourceFallback ??
(project.sources.isNotEmpty
? SourceFallback(
sources: project.sources,
builder: const NoOpSourceBuilder(),
preparation: [],
)
: null);
if (effectiveFallback == null) {
throw StateError(
'No source provided and no source fallback configured for '
'${project.name} on ${target.label}.',
);
}
logger?.info('Resolving source...');
final sourceResult = await SourceFallbackResolver().resolve(
fallback: effectiveFallback,
packageRoot: Directory.current,
sourceCacheRoot: Directory(
p.join(
Directory.current.path,
'.dart_tool',
'native_prebuilt',
'sources',
),
),
input: _dummyInput(),
output: _dummyOutput(),
logger: logger,
);
if (sourceResult == null) {
throw StateError(
'Failed to resolve source for ${project.name} on ${target.label}.',
);
}
resolvedSource = sourceResult.source;
workDir ??= sourceResult.workDirectory;
}
// 3. Create build context
final context = NativeBuildContext(
target: target,
hook: NativeHookConfiguration(
packageName: project.name,
assetName: project.asset.assetName,
libraryStem: project.asset.libraryStem,
linkMode: linkMode ?? project.asset.linkMode,
),
directories: NativeBuildDirectories(
source: resolvedSource.directory,
output: outputDir,
cache:
workDir ??
Directory(
p.join(
resolvedSource.directory.path,
'.dart_tool',
'native_prebuilt',
),
),
work:
workDir ??
Directory(
p.join(
resolvedSource.directory.path,
'.dart_tool',
'native_prebuilt',
),
),
),
toolchains: const ToolchainRegistry(),
environment: Platform.environment,
options: project.build.options,
variables: project.build.variables,
logger: logger,
);
// 4. Execute the recipe
logger?.info('Executing recipe: ${recipe.runtimeType}...');
// Inject cache into StepBuildRecipe if available
NativeBuildRecipe effectiveRecipe = recipe;
if (cache != null && recipe is StepBuildRecipe) {
effectiveRecipe = StepBuildRecipe(
steps: recipe.steps,
needsById: recipe.needsById,
cache: cache,
);
}
final result = await effectiveRecipe.execute(context, resolvedSource);
// 5. Stage artifact bundle
await _stageArtifacts(context, result);
// 6. Write metadata
await _writeMetadata(context, result, target);
logger?.info(
'Build completed: ${result.artifacts.length} artifact(s) staged '
'to ${outputDir.path}',
);
return result;
}