The migration system converts various build systems to Builder's Builderfile format. It uses a parse-transform-emit architecture with an intermediate representation (IR).
Input File
│
▼
IMigrator (Interface)
│
▼
Parse & Extract (System-specific)
│
▼
MigrationTarget (IR)
│
▼
BuilderfileEmitter (DSL generation)
│
▼
Builderfile
-
Unified IR: All build systems share common concepts (targets, sources, dependencies). The IR captures these universals.
-
Registry Pattern: Central registration of migrators with factory creation and auto-detection.
-
Result-Based Errors: All operations return
Result!(T, BuildError)for type-safe error handling. -
Strong Typing: Uses
Target,TargetType,TargetLanguagefrom schema for compile-time safety.
infrastructure/migration/
├── core/
│ ├── common.d - IR types (MigrationTarget, MigrationResult)
│ ├── base.d - IMigrator interface, BaseMigrator
│ └── package.d
├── registry/
│ ├── registry.d - MigratorRegistry (singleton)
│ └── package.d
├── emission/
│ ├── emitter.d - BuilderfileEmitter (DSL generation)
│ └── package.d
├── systems/ - Individual migrators
│ ├── bazel.d
│ ├── cmake.d
│ ├── maven.d
│ ├── gradle.d
│ ├── make.d
│ ├── cargo.d
│ ├── npm.d
│ ├── gomod.d
│ ├── dub.d
│ ├── sbt.d
│ ├── meson.d
│ └── package.d
└── package.d
Intermediate representation:
struct MigrationTarget
{
string name;
TargetType type; // Executable, Library, Test, Custom
TargetLanguage language; // C, Cpp, Python, Go, etc.
string[] sources;
string[] dependencies;
string[] flags;
string[] includes;
string output;
string[string] env;
string[string] metadata; // System-specific data
Target toTarget() const; // Convert to Builder schema
}struct MigrationResult
{
MigrationTarget[] targets;
MigrationWarning[] warnings;
string[string] globalConfig;
bool success;
bool hasErrors() const;
bool hasWarnings() const;
MigrationWarning[] errors() const;
}interface IMigrator
{
string systemName() const;
string[] defaultFileNames() const;
bool canMigrate(string filePath) const;
Result!(MigrationResult, BuildError) migrate(string inputPath);
string description() const;
string[] supportedFeatures() const;
string[] limitations() const;
}enum WarningLevel
{
Info, // Informational
Warning, // Should review
Error // Critical issue
}
struct MigrationWarning
{
WarningLevel level;
string message;
string context;
string[] suggestions;
}| System | File | Language Inference |
|---|---|---|
| Bazel | BUILD, BUILD.bazel | Rule names (cc_binary → C++) |
| CMake | CMakeLists.txt | Commands (add_executable → C++) |
| Maven | pom.xml | <packaging> element |
| Gradle | build.gradle, build.gradle.kts | Plugins applied |
| Make | Makefile | Targets and recipes |
| Cargo | Cargo.toml | Always Rust |
| npm | package.json | Scripts and dependencies |
| Go | go.mod | Always Go |
| DUB | dub.json, dub.sdl | Always D |
| SBT | build.sbt | Always Scala |
| Meson | meson.build | project() language |
Each migrator:
- Reads input file once
- Extracts all relevant information
- Transforms to IR immediately
- Validates during extraction
No multi-pass parsing.
| Format | Approach |
|---|---|
| JSON | Standard library |
| Regex | Simple pattern matching (Bazel, Make) |
| XML | Pattern matching (Maven) |
| Custom | Context-specific (CMake, Meson) |
Location: source/infrastructure/migration/emission/emitter.d
Generates clean DSL:
- Indentation-aware nesting
- Idiomatic syntax
- Warnings as comments
- Metadata preserved
Example output:
// Builderfile
// Auto-generated by Builder migration tool
// Review and adjust as needed
target("hello") {
type: executable;
language: cpp;
sources: ["main.cpp", "greeter.cpp"];
deps: [":utils"];
flags: ["-std=c++17", "-Wall"];
// Additional metadata:
// linkopts: -lpthread
}
// Migration Summary
// =================
// WARNINGS:
// - Linker flags require manual configuration
// Context: linkoptsLocation: source/infrastructure/migration/registry/registry.d
class MigratorRegistry
{
static MigratorRegistry getInstance();
void register(IMigrator migrator);
IMigrator create(string systemName);
bool isSupported(string systemName) const;
string[] availableSystems() const;
IMigrator[] allMigrators();
}auto migrator = MigratorFactory.autoDetect(filePath);
if (migrator !is null)
{
auto result = migrator.migrate(filePath);
if (result.isOk)
{
auto emitter = BuilderfileEmitter();
auto builderfile = emitter.emit(result.unwrap());
writeFile("Builderfile", builderfile);
}
}auto migrator = MigratorFactory.create("bazel");
auto result = migrator.migrate("BUILD");# Auto-detect
bldr migrate BUILD
# Explicit system
bldr migrate --from=cmake CMakeLists.txt
# Dry run (show output, don't write)
bldr migrate --dry-run pom.xml
# List supported systems
bldr migrate --list- Create migrator class:
final class NewSystemMigrator : BaseMigrator
{
override string systemName() const { return "newsystem"; }
override string[] defaultFileNames() const { return ["build.new"]; }
override bool canMigrate(string path) const { /* ... */ }
override Result!(MigrationResult, BuildError) migrate(string path)
{
auto contentResult = readInputFile(path);
if (contentResult.isErr)
return contentResult.err;
auto content = contentResult.unwrap();
MigrationTarget[] targets;
MigrationWarning[] warnings;
// Parse and extract...
return createResult(targets, warnings);
}
}- Register in
registry.d:
private void registerMigrators()
{
register(new NewSystemMigrator());
// ...
}- Add to systems package:
public import infrastructure.migration.systems.newsystem;Migration is I/O bound:
- Small files (<100 lines): <10ms
- Medium files (100-1000 lines): <50ms
- Large files (>1000 lines): <200ms
Strategies:
- Compiled regex (reused)
- Single-pass parsing
- No backtracking
Unit tests per migrator:
- Valid input parsing
- Edge cases (empty, minimal, maximal)
- Error handling
- Feature coverage
Integration tests:
- Auto-detection
- CLI commands
- End-to-end migration
Real-world validation:
- Test against actual project build files
- Compare output to manual migration