forked from bufbuild/buf
/
module_file_set_builder.go
111 lines (105 loc) · 4.45 KB
/
module_file_set_builder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Copyright 2020-2024 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bufmodulebuild
import (
"context"
"github.com/alis-exchange/buf/private/bufpkg/bufmodule"
"go.uber.org/zap"
)
type moduleFileSetBuilder struct {
logger *zap.Logger
moduleReader bufmodule.ModuleReader
}
func newModuleFileSetBuilder(
logger *zap.Logger,
moduleReader bufmodule.ModuleReader,
) *moduleFileSetBuilder {
return &moduleFileSetBuilder{
logger: logger,
moduleReader: moduleReader,
}
}
func (m *moduleFileSetBuilder) Build(
ctx context.Context,
module bufmodule.Module,
options ...BuildModuleFileSetOption,
) (bufmodule.ModuleFileSet, error) {
buildModuleFileSetOptions := &buildModuleFileSetOptions{}
for _, option := range options {
option(buildModuleFileSetOptions)
}
return m.build(
ctx,
module,
buildModuleFileSetOptions.workspace,
)
}
func (m *moduleFileSetBuilder) build(
ctx context.Context,
module bufmodule.Module,
workspace bufmodule.Workspace,
) (bufmodule.ModuleFileSet, error) {
var dependencyModules []bufmodule.Module
moduleWorkspaceDirectory := module.WorkspaceDirectory()
if workspace != nil {
// From the perspective of the ModuleFileSet, we include all of the files
// specified in the workspace. When we build the Image from the ModuleFileSet,
// we construct it based on the TargetFileInfos, and thus only include the files
// in the transitive closure.
//
// This is defensible as we're saying that everything in the workspace is a potential
// dependency, even if some are not actual dependencies of this specific module. In this
// case, the extra modules are no different than unused dependencies in a buf.yaml/buf.lock.
//
// By including all the Modules from the workspace, we are potentially including the input
// Module itself. This is bad, and will result in errors when using the result ModuleFileSet.
// The ModuleFileSet expects a Module, and its dependency Modules, but it is not OK for
// a Module to both be the input Module and a dependency Module. This means we need to check
// each module in the workspace to see if it is the same as the input module, and only add
// it as a dependency if it's not the same as the input module. We determine this by comparing
// each workspace module's WorkspaceDirectory with the input module's WorkspaceDirectory,
// where having the same WorkspaceDirectory means being the same module. This is correct
// because each module in a workspace are constructed with its WorkspaceDirectory set to its
// path relative to buf.work.yaml and this value is guaranteed to be unique within the same
// workspace. This is predicated on the input module belonging to the workspace, and it would
// be a bug if the input module doesn't belong to this workspace.
//
// We could also determine which modules could be omitted here, but it would incur
// the cost of parsing the target files and detecting exactly which imports are
// used. We already get this for free in Image construction, so it's simplest and
// most efficient to bundle all of the modules together like so.
for _, potentialDependencyModule := range workspace.GetModules() {
if moduleWorkspaceDirectory != potentialDependencyModule.WorkspaceDirectory() {
dependencyModules = append(dependencyModules, potentialDependencyModule)
}
}
}
// We know these are unique by remote, owner, repository and
// contain all transitive dependencies.
for _, dependencyModulePin := range module.DependencyModulePins() {
if workspace != nil {
if _, ok := workspace.GetModule(dependencyModulePin); ok {
// This dependency is already provided by the workspace, so we don't
// need to consult the ModuleReader.
continue
}
}
dependencyModule, err := m.moduleReader.GetModule(ctx, dependencyModulePin)
if err != nil {
return nil, err
}
dependencyModules = append(dependencyModules, dependencyModule)
}
return bufmodule.NewModuleFileSet(module, dependencyModules), nil
}