generateConfigurableHomebrewFormula function
String
generateConfigurableHomebrewFormula({
- required HomebrewFormulaConfig config,
- required List<
HomebrewArtifact> artifacts,
Renders a product-neutral Formula for the supplied artifacts.
Only the platforms and architectures present in artifacts are rendered,
so a product that does not ship, say, a Linux arm64 build produces a
Formula that simply does not claim to support it.
Implementation
String generateConfigurableHomebrewFormula({
required HomebrewFormulaConfig config,
required List<HomebrewArtifact> artifacts,
}) {
if (artifacts.isEmpty) {
throw const ShipworldException(
'A Homebrew Formula needs at least one artifact',
code: 'invalid_config',
);
}
HomebrewArtifact? find(String platform, String architecture) {
for (final item in artifacts) {
if (item.platform == platform && item.architecture == architecture) {
return item;
}
}
return null;
}
/// Renders `on_macos`/`on_linux` with only the architectures that exist.
String? platformBlock(String platform) {
final arm = find(platform, 'arm64');
final intel = find(platform, 'x64');
if (arm == null && intel == null) return null;
final blocks = <String>[
// Homebrew's DSL names them by CPU family, not by the architecture
// strings this package uses everywhere else.
if (intel != null)
'''
on_intel do
url "${intel.url}"
sha256 "${intel.sha256}"
end''',
if (arm != null)
'''
on_arm do
url "${arm.url}"
sha256 "${arm.sha256}"
end''',
];
return ' on_$platform do\n${blocks.join('\n')}\n end';
}
final platforms = <String>[
for (final platform in const ['macos', 'linux']) ?platformBlock(platform),
];
final kegOnly = config.versioned ? '\n keg_only :versioned_formula\n' : '';
// Homebrew strips the single top-level directory of the archive, so the
// staging directory holds the bundle's own `bin/` and `lib/`. The launcher
// is symlinked rather than copied because its RPATH is relative to the real
// path of the executable, which keeps `lib/` reachable through the link.
final install = switch (config.payload) {
PayloadKind.directory =>
'''
libexec.install Dir["*"]
bin.install_symlink libexec/"bin/${config.executableName}"''',
PayloadKind.executable => _executableInstall(config, find),
};
return '''
class ${config.className} < Formula
desc "${config.description}"
homepage "${config.homepage}"
version "${config.version}"$kegOnly
${platforms.join('\n\n')}
def install
$install
end
test do
system "#{bin}/${config.executableName}", "--version"
end
end
''';
}