From a44da806d48e5329ec390b8ca022af14ef08886e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=A5=D1=80?= =?UTF-8?q?=D0=B0=D0=BC=D0=BE=D0=B2?= Date: Mon, 15 Dec 2025 22:36:49 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B2=D1=82=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=D0=BE=D0=B2=20=D0=BC?= =?UTF-8?q?=D1=83=D0=BB=D1=8C=D1=82=D0=B8=D0=BF=D0=B0=D0=BA=D0=B5=D1=82?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit При установке пакета с зависимостями на другие подпакеты того же мультипакета теперь каждый подпакет собирается только один раз. --- internal/build/build.go | 106 +++++++++++++++++------------- internal/build/dependency_tree.go | 54 +++++++-------- 2 files changed, 84 insertions(+), 76 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index a7f220d..ea76127 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -39,12 +39,13 @@ import ( ) type BuildInput struct { - opts *types.BuildOpts - info *distro.OSRelease - pkgFormat string - script string - repository string - packages []string + opts *types.BuildOpts + info *distro.OSRelease + pkgFormat string + script string + repository string + packages []string + skipDepsBuilding bool // Пропустить сборку зависимостей (используется при вызове из BuildALRDeps) } func (bi *BuildInput) GobEncode() ([]byte, error) { @@ -242,9 +243,10 @@ type Builder struct { } type BuildArgs struct { - Opts *types.BuildOpts - Info *distro.OSRelease - PkgFormat_ string + Opts *types.BuildOpts + Info *distro.OSRelease + PkgFormat_ string + SkipDepsBuilding bool // Пропустить сборку зависимостей (используется при вызове из BuildALRDeps) } func (b *BuildArgs) BuildOpts() *types.BuildOpts { @@ -278,12 +280,13 @@ func (b *Builder) BuildPackageFromDb( scriptInfo := b.scriptResolver.ResolveScript(ctx, args.Package) return b.BuildPackage(ctx, &BuildInput{ - script: scriptInfo.Script, - repository: scriptInfo.Repository, - packages: args.Packages, - pkgFormat: args.PkgFormat(), - opts: args.Opts, - info: args.Info, + script: scriptInfo.Script, + repository: scriptInfo.Repository, + packages: args.Packages, + pkgFormat: args.PkgFormat(), + opts: args.Opts, + info: args.Info, + skipDepsBuilding: args.SkipDepsBuilding, }) } @@ -407,13 +410,14 @@ func (b *Builder) BuildPackage( // We filter so as not to re-build what has already been built at the `installBuildDeps` stage. var filteredDepends []string - + // Создаем набор подпакетов текущего мультипакета для исключения циклических зависимостей + // Используем имена из varsOfPackages, так как input.packages может быть пустым currentPackageNames := make(map[string]struct{}) - for _, pkg := range input.packages { - currentPackageNames[pkg] = struct{}{} + for _, vars := range varsOfPackages { + currentPackageNames[vars.Name] = struct{}{} } - + for _, d := range depends { if _, found := depNames[d]; !found { // Исключаем зависимости, которые являются подпакетами текущего мультипакета @@ -423,10 +427,16 @@ func (b *Builder) BuildPackage( } } - slog.Debug("BuildALRDeps") - newBuiltDeps, repoDeps, err := b.BuildALRDeps(ctx, input, filteredDepends) - if err != nil { - return nil, err + var newBuiltDeps []*BuiltDep + var repoDeps []string + + // Пропускаем сборку зависимостей если флаг установлен (вызов из BuildALRDeps) + if !input.skipDepsBuilding { + slog.Debug("BuildALRDeps") + newBuiltDeps, repoDeps, err = b.BuildALRDeps(ctx, input, filteredDepends) + if err != nil { + return nil, err + } } slog.Debug("PrepareDirs") @@ -580,65 +590,68 @@ func (b *Builder) BuildALRDeps( // Системные зависимости возвращаем как repoDeps repoDeps = systemDeps - // Шаг 2: Собираем список всех пакетов из дерева для топологической сортировки - allFound := make(map[string][]alrsh.Package) - for baseName, node := range depTree { - allFound[baseName] = []alrsh.Package{*node.Package} - } - - // Шаг 3: Топологическая сортировка (от корней к листьям) - sortedPkgs, err := TopologicalSort(depTree, allFound) + // Шаг 2: Топологическая сортировка (от корней к листьям) + sortedPkgs, err := TopologicalSort(depTree) if err != nil { return nil, nil, fmt.Errorf("failed to sort dependencies: %w", err) } + // Шаг 3: Группируем подпакеты по basePkgName для оптимизации сборки + // Если несколько подпакетов из одного мультипакета, собираем их вместе + builtBasePkgs := make(map[string]bool) // Уже собранные базовые пакеты + // Шаг 4: Собираем пакеты в правильном порядке, проверяя кеш - for _, basePkgName := range sortedPkgs { - node := depTree[basePkgName] + for _, pkgName := range sortedPkgs { + node := depTree[pkgName] if node == nil { continue } pkg := node.Package + basePkgName := node.BasePkgName - // Находим ВСЕ подпакеты с этим BasePkgName - allSubpkgs, err := b.findAllSubpackages(ctx, basePkgName, pkg.Repository) - if err != nil { - return nil, nil, fmt.Errorf("failed to find subpackages for %s: %w", basePkgName, err) + // Если базовый пакет уже собран, пропускаем + if builtBasePkgs[basePkgName] { + continue } - // Проверяем кеш для ВСЕХ подпакетов + // Собираем только запрошенный подпакет (или все, если запрошен basePkgName) + packagesToBuilt := []string{pkgName} + + // Проверяем кеш для запрошенного подпакета scriptInfo := b.scriptResolver.ResolveScript(ctx, pkg) buildInput := &BuildInput{ script: scriptInfo.Script, repository: scriptInfo.Repository, - packages: allSubpkgs, + packages: packagesToBuilt, pkgFormat: input.PkgFormat(), opts: input.BuildOpts(), info: input.OSRelease(), } - cachedDeps, allInCache, err := b.checkCacheForAllSubpackages(ctx, buildInput, basePkgName, allSubpkgs) + cachedDeps, allInCache, err := b.checkCacheForAllSubpackages(ctx, buildInput, basePkgName, packagesToBuilt) if err != nil { return nil, nil, err } if allInCache { - // Все подпакеты в кеше, используем их + // Подпакет в кеше, используем его buildDeps = append(buildDeps, cachedDeps...) continue } - // Собираем пакет (без рекурсивной сборки зависимостей, так как они уже собраны) + // Собираем только запрошенный подпакет + // SkipDepsBuilding: true предотвращает рекурсивный вызов BuildALRDeps res, err := b.BuildPackageFromDb( ctx, &BuildPackageFromDbArgs{ Package: pkg, - Packages: allSubpkgs, + Packages: packagesToBuilt, BuildArgs: BuildArgs{ - Opts: input.BuildOpts(), - Info: input.OSRelease(), - PkgFormat_: input.PkgFormat(), + Opts: input.BuildOpts(), + Info: input.OSRelease(), + PkgFormat_: input.PkgFormat(), + SkipDepsBuilding: true, }, }, ) @@ -647,6 +660,7 @@ func (b *Builder) BuildALRDeps( } buildDeps = append(buildDeps, res...) + builtBasePkgs[basePkgName] = true } buildDeps = removeDuplicates(buildDeps) diff --git a/internal/build/dependency_tree.go b/internal/build/dependency_tree.go index 0612ef5..c00efaa 100644 --- a/internal/build/dependency_tree.go +++ b/internal/build/dependency_tree.go @@ -27,6 +27,7 @@ import ( type DependencyNode struct { Package *alrsh.Package BasePkgName string + PkgName string // Имя конкретного подпакета (может отличаться от BasePkgName) Dependencies []string // Имена зависимостей } @@ -82,8 +83,9 @@ func (b *Builder) ResolveDependencyTree( baseName = pkg.Name } - // Если уже обработали этот базовый пакет, пропускаем - if resolved[baseName] != nil { + // Используем имя конкретного подпакета как ключ (не basePkgName) + // Это позволяет собирать только запрошенный подпакет, а не весь мультипакет + if resolved[pkgName] != nil { continue } @@ -96,10 +98,11 @@ func (b *Builder) ResolveDependencyTree( allDeps := append([]string{}, deps...) allDeps = append(allDeps, buildDeps...) - // Добавляем узел в resolved - resolved[baseName] = &DependencyNode{ + // Добавляем узел в resolved с ключом = имя подпакета + resolved[pkgName] = &DependencyNode{ Package: &pkg, BasePkgName: baseName, + PkgName: pkgName, Dependencies: allDeps, } @@ -129,8 +132,8 @@ func (b *Builder) ResolveDependencyTree( } // TopologicalSort выполняет топологическую сортировку пакетов по зависимостям -// Возвращает список базовых имен пакетов в порядке сборки (от корней к листьям) -func TopologicalSort(nodes map[string]*DependencyNode, allPkgs map[string][]alrsh.Package) ([]string, error) { +// Возвращает список имен подпакетов в порядке сборки (от корней к листьям) +func TopologicalSort(nodes map[string]*DependencyNode) ([]string, error) { // Список для результата var result []string @@ -140,51 +143,42 @@ func TopologicalSort(nodes map[string]*DependencyNode, allPkgs map[string][]alrs // Множество узлов в текущем пути (для обнаружения циклов) inStack := make(map[string]bool) - var visit func(basePkgName string) error - visit = func(basePkgName string) error { - if visited[basePkgName] { + var visit func(pkgName string) error + visit = func(pkgName string) error { + if visited[pkgName] { return nil } - if inStack[basePkgName] { - return fmt.Errorf("circular dependency detected: %s", basePkgName) + if inStack[pkgName] { + return fmt.Errorf("circular dependency detected: %s", pkgName) } - node := nodes[basePkgName] + node := nodes[pkgName] if node == nil { - // Это системный пакет, игнорируем + // Это системный пакет или пакет не в дереве, игнорируем return nil } - inStack[basePkgName] = true + inStack[pkgName] = true // Посещаем все зависимости for _, dep := range node.Dependencies { - // Находим базовое имя для зависимости - depBaseName := dep - - // Проверяем, есть ли этот пакет в allPkgs - if pkgs, ok := allPkgs[dep]; ok && len(pkgs) > 0 { - if pkgs[0].BasePkgName != "" { - depBaseName = pkgs[0].BasePkgName - } - } - - if err := visit(depBaseName); err != nil { + // Используем имя зависимости напрямую (это имя подпакета) + if err := visit(dep); err != nil { return err } } - inStack[basePkgName] = false - visited[basePkgName] = true - result = append(result, basePkgName) + inStack[pkgName] = false + visited[pkgName] = true + result = append(result, pkgName) return nil } // Посещаем все узлы - for basePkgName := range nodes { - if err := visit(basePkgName); err != nil { + for pkgName := range nodes { + if err := visit(pkgName); err != nil { return nil, err } } -- 2.49.1