Skip to content
Merged
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
86 changes: 57 additions & 29 deletions internal/core/services/compilerselector/selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,50 +67,78 @@ func (s *Service) SelectCompiler(ctx SelectionContext) (*SelectedCompiler, error
return nil, errors.New("no Delphi installation found")
}

targetPlatform := resolveTargetPlatform(ctx)

if ctx.CliCompilerVersion != "" {
return findCompiler(installations, ctx.CliCompilerVersion, ctx.CliPlatform)
return findCompiler(installations, ctx.CliCompilerVersion, targetPlatform)
}

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

platform := tc.Platform
if platform == "" {
platform = consts.PlatformWin32.String()
}

if tc.Compiler != "" {
return findCompiler(installations, tc.Compiler, platform)
}
if ctx.Package != nil && ctx.Package.Toolchain != nil && ctx.Package.Toolchain.Compiler != "" {
return findCompiler(installations, ctx.Package.Toolchain.Compiler, targetPlatform)
}

globalPath := s.config.GetDelphiPath()
if globalPath != "" {
for _, inst := range installations {
instDir := filepath.Dir(inst.Path)
if strings.EqualFold(instDir, globalPath) {
return createSelectedCompiler(inst), nil
}
return findGlobalPathCompiler(installations, globalPath, targetPlatform)
}

return findLatestCompiler(installations, targetPlatform)
}

func resolveTargetPlatform(ctx SelectionContext) string {
switch {
case ctx.CliPlatform != "":
return ctx.CliPlatform
case ctx.Package != nil && ctx.Package.Toolchain != nil && ctx.Package.Toolchain.Platform != "":
return ctx.Package.Toolchain.Platform
default:
return consts.PlatformWin32.String()
}
}

func findGlobalPathCompiler(installations []registryadapter.DelphiInstallation, globalPath, targetPlatform string) (*SelectedCompiler, error) {
for _, inst := range installations {
instDir := filepath.Dir(inst.Path)
if strings.EqualFold(instDir, globalPath) && strings.EqualFold(inst.Arch, targetPlatform) {
return createSelectedCompiler(inst), nil
}
}

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

if len(installations) > 0 {
latest := installations[0]
for _, inst := range installations[1:] {
if inst.Version > latest.Version {
latest = inst
func findLatestCompiler(installations []registryadapter.DelphiInstallation, targetPlatform string) (*SelectedCompiler, error) {
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
}
}
return createSelectedCompiler(latest), nil
}
if bestMatch != nil {
return createSelectedCompiler(*bestMatch), nil
}

return nil, errors.New("could not determine compiler")
latest := installations[0]
for _, inst := range installations[1:] {
if inst.Version > latest.Version {
latest = inst
}
}
return createSelectedCompiler(latest), nil
}

//nolint:lll // Function signature cannot be easily shortened
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