Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 43 additions & 15 deletions internal/core/services/compilerselector/selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,39 +67,67 @@ func (s *Service) SelectCompiler(ctx SelectionContext) (*SelectedCompiler, error
return nil, errors.New("no Delphi installation found")
}

if ctx.CliCompilerVersion != "" {
return findCompiler(installations, ctx.CliCompilerVersion, ctx.CliPlatform)
// Determine target platform
var targetPlatform string
if ctx.CliPlatform != "" {
targetPlatform = ctx.CliPlatform
} else if ctx.Package != nil && ctx.Package.Toolchain != nil && ctx.Package.Toolchain.Platform != "" {
targetPlatform = ctx.Package.Toolchain.Platform
} else {
targetPlatform = consts.PlatformWin32.String()
}

if ctx.Package != nil && ctx.Package.Toolchain != nil {
tc := ctx.Package.Toolchain

platform := tc.Platform
if platform == "" {
platform = consts.PlatformWin32.String()
}
// 1. If CLI compiler version is specified
if ctx.CliCompilerVersion != "" {
return findCompiler(installations, ctx.CliCompilerVersion, targetPlatform)
}

if tc.Compiler != "" {
return findCompiler(installations, tc.Compiler, platform)
}
// 2. If toolchain compiler version is specified
if ctx.Package != nil && ctx.Package.Toolchain != nil && ctx.Package.Toolchain.Compiler != "" {
return findCompiler(installations, ctx.Package.Toolchain.Compiler, targetPlatform)
}

// 3. Fallback to global path
globalPath := s.config.GetDelphiPath()
if globalPath != "" {
// Try to find a matching installation for the target platform in the global path
for _, inst := range installations {
instDir := filepath.Dir(inst.Path)
if strings.EqualFold(instDir, globalPath) {
if strings.EqualFold(instDir, globalPath) && strings.EqualFold(inst.Arch, targetPlatform) {
return createSelectedCompiler(inst), nil
}
}

// Fallback if not found in registered installations
var compilerBinary string
switch targetPlatform {
case consts.PlatformWin64.String():
compilerBinary = "dcc64.exe"
default:
compilerBinary = "dcc32.exe"
}
return &SelectedCompiler{
Path: filepath.Join(globalPath, "dcc32.exe"),
Path: filepath.Join(globalPath, compilerBinary),
BinDir: globalPath,
Arch: consts.PlatformWin32.String(),
Arch: targetPlatform,
}, nil
}

// 4. Fallback to the latest installation that matches the target platform
var bestMatch *registryadapter.DelphiInstallation
for _, inst := range installations {
if strings.EqualFold(inst.Arch, targetPlatform) {
if bestMatch == nil || inst.Version > bestMatch.Version {
instCopy := inst
bestMatch = &instCopy
}
}
}
if bestMatch != nil {
return createSelectedCompiler(*bestMatch), nil
}

// If no installation matching the target platform is found, fallback to the latest overall installation
if len(installations) > 0 {
latest := installations[0]
for _, inst := range installations[1:] {
Expand Down
172 changes: 172 additions & 0 deletions internal/core/services/compilerselector/selector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package compilerselector_test

import (
"path/filepath"
"testing"

registryadapter "github.com/hashload/boss/internal/adapters/secondary/registry"
"github.com/hashload/boss/internal/core/domain"
"github.com/hashload/boss/internal/core/services/compilerselector"
"github.com/hashload/boss/pkg/env"
)

// mockRegistryAdapter is a mock for compilerselector.RegistryAdapter
type mockRegistryAdapter struct {
installations []registryadapter.DelphiInstallation
}

func (m *mockRegistryAdapter) GetDetectedDelphis() []registryadapter.DelphiInstallation {
return m.installations
}

// mockConfigProvider embeds env.ConfigProvider to mock only GetDelphiPath
type mockConfigProvider struct {
env.ConfigProvider
delphiPath string
}

func (m *mockConfigProvider) GetDelphiPath() string {
return m.delphiPath
}

func TestSelectCompiler_CliOverrides(t *testing.T) {
registry := &mockRegistryAdapter{
installations: []registryadapter.DelphiInstallation{
{Version: "35.0", Path: filepath.Join("C:", "Delphi35", "bin", "dcc32.exe"), Arch: "Win32"},
{Version: "35.0", Path: filepath.Join("C:", "Delphi35", "bin", "dcc64.exe"), Arch: "Win64"},
{Version: "36.0", Path: filepath.Join("C:", "Delphi36", "bin", "dcc32.exe"), Arch: "Win32"},
{Version: "36.0", Path: filepath.Join("C:", "Delphi36", "bin", "dcc64.exe"), Arch: "Win64"},
},
}
config := &mockConfigProvider{}
service := compilerselector.NewService(registry, config)

// CLI version and platform specified
ctx := compilerselector.SelectionContext{
CliCompilerVersion: "35.0",
CliPlatform: "Win64",
Package: &domain.Package{
Toolchain: &domain.PackageToolchain{
Compiler: "36.0",
Platform: "Win32",
},
},
}

selected, err := service.SelectCompiler(ctx)
if err != nil {
t.Fatalf("Failed to select compiler: %v", err)
}

if selected.Version != "35.0" {
t.Errorf("Expected version 35.0, got %s", selected.Version)
}
if selected.Arch != "Win64" {
t.Errorf("Expected arch Win64, got %s", selected.Arch)
}
}

func TestSelectCompiler_ToolchainCompilerAndPlatform(t *testing.T) {
registry := &mockRegistryAdapter{
installations: []registryadapter.DelphiInstallation{
{Version: "35.0", Path: filepath.Join("C:", "Delphi35", "bin", "dcc32.exe"), Arch: "Win32"},
{Version: "35.0", Path: filepath.Join("C:", "Delphi35", "bin", "dcc64.exe"), Arch: "Win64"},
{Version: "36.0", Path: filepath.Join("C:", "Delphi36", "bin", "dcc32.exe"), Arch: "Win32"},
{Version: "36.0", Path: filepath.Join("C:", "Delphi36", "bin", "dcc64.exe"), Arch: "Win64"},
},
}
config := &mockConfigProvider{}
service := compilerselector.NewService(registry, config)

// Toolchain compiler and platform specified
ctx := compilerselector.SelectionContext{
Package: &domain.Package{
Toolchain: &domain.PackageToolchain{
Compiler: "36.0",
Platform: "Win64",
},
},
}

selected, err := service.SelectCompiler(ctx)
if err != nil {
t.Fatalf("Failed to select compiler: %v", err)
}

if selected.Version != "36.0" {
t.Errorf("Expected version 36.0, got %s", selected.Version)
}
if selected.Arch != "Win64" {
t.Errorf("Expected arch Win64, got %s", selected.Arch)
}
}

func TestSelectCompiler_ToolchainPlatformOnly(t *testing.T) {
registry := &mockRegistryAdapter{
installations: []registryadapter.DelphiInstallation{
{Version: "35.0", Path: filepath.Join("C:", "Delphi35", "bin", "dcc32.exe"), Arch: "Win32"},
{Version: "35.0", Path: filepath.Join("C:", "Delphi35", "bin", "dcc64.exe"), Arch: "Win64"},
{Version: "36.0", Path: filepath.Join("C:", "Delphi36", "bin", "dcc32.exe"), Arch: "Win32"},
{Version: "36.0", Path: filepath.Join("C:", "Delphi36", "bin", "dcc64.exe"), Arch: "Win64"},
},
}
config := &mockConfigProvider{}
service := compilerselector.NewService(registry, config)

// Only platform Win64 specified in toolchain
ctx := compilerselector.SelectionContext{
Package: &domain.Package{
Toolchain: &domain.PackageToolchain{
Platform: "Win64",
},
},
}

selected, err := service.SelectCompiler(ctx)
if err != nil {
t.Fatalf("Failed to select compiler: %v", err)
}

// Should select the latest version (36.0) matching platform Win64
if selected.Version != "36.0" {
t.Errorf("Expected version 36.0, got %s", selected.Version)
}
if selected.Arch != "Win64" {
t.Errorf("Expected arch Win64, got %s", selected.Arch)
}
}

func TestSelectCompiler_FallbackToGlobalPath(t *testing.T) {
registry := &mockRegistryAdapter{
installations: []registryadapter.DelphiInstallation{
{Version: "35.0", Path: filepath.Join("C:", "Delphi35", "bin", "dcc32.exe"), Arch: "Win32"},
},
}
globalPath := filepath.Join("C:", "CustomDelphi", "bin")
config := &mockConfigProvider{
delphiPath: globalPath,
}
service := compilerselector.NewService(registry, config)

// Win64 requested, should fallback to dcc64.exe in globalPath
ctx := compilerselector.SelectionContext{
Package: &domain.Package{
Toolchain: &domain.PackageToolchain{
Platform: "Win64",
},
},
}

selected, err := service.SelectCompiler(ctx)
if err != nil {
t.Fatalf("Failed to select compiler: %v", err)
}

expectedPath := filepath.Join(globalPath, "dcc64.exe")
if selected.Path != expectedPath {
t.Errorf("Expected path %s, got %s", expectedPath, selected.Path)
}
if selected.Arch != "Win64" {
t.Errorf("Expected arch Win64, got %s", selected.Arch)
}
}
Loading