@@ -48,18 +48,29 @@ func (d *DerivationBuilder) init() error {
48
48
// Build applies patches to a package store path and puts the result in the
49
49
// d.Out directory.
50
50
func (d * DerivationBuilder ) Build (ctx context.Context , pkgStorePath string ) error {
51
- slog .DebugContext (ctx , "starting build of patched package" , "pkg" , pkgStorePath , "out" , d .Out )
51
+ if err := d .init (); err != nil {
52
+ return err
53
+ }
52
54
55
+ slog .DebugContext (ctx , "starting build to patch package" ,
56
+ "pkg" , pkgStorePath , "out" , d .Out )
57
+ return d .build (ctx , newPackageFS (pkgStorePath ), newPackageFS (d .Out ))
58
+ }
59
+
60
+ func (d * DerivationBuilder ) build (ctx context.Context , pkg , out * packageFS ) error {
53
61
var err error
54
- pkgFS := os .DirFS (pkgStorePath )
55
- for path , entry := range allFiles (pkgFS , "." ) {
62
+ for path , entry := range allFiles (pkg , "." ) {
63
+ if ctx .Err () != nil {
64
+ return err
65
+ }
66
+
56
67
switch {
57
68
case entry .IsDir ():
58
- err = d .copyDir (path )
69
+ err = d .copyDir (out , path )
59
70
case isSymlink (entry .Type ()):
60
- err = d .copySymlink (pkgStorePath , path )
71
+ err = d .copySymlink (pkg , out , path )
61
72
default :
62
- err = d .copyFile (pkgFS , path )
73
+ err = d .copyFile (pkg , out , path )
63
74
}
64
75
65
76
if err != nil {
@@ -74,16 +85,16 @@ func (d *DerivationBuilder) Build(ctx context.Context, pkgStorePath string) erro
74
85
return cmd .Run ()
75
86
}
76
87
77
- func (d * DerivationBuilder ) copyDir (path string ) error {
78
- osPath , err := filepath . Localize (path )
88
+ func (d * DerivationBuilder ) copyDir (out * packageFS , path string ) error {
89
+ path , err := out . OSPath (path )
79
90
if err != nil {
80
91
return err
81
92
}
82
- return os .Mkdir (filepath . Join ( d . Out , osPath ) , 0o777 )
93
+ return os .Mkdir (path , 0o777 )
83
94
}
84
95
85
- func (d * DerivationBuilder ) copyFile (pkgFS fs. FS , path string ) error {
86
- src , err := pkgFS .Open (path )
96
+ func (d * DerivationBuilder ) copyFile (pkg , out * packageFS , path string ) error {
97
+ src , err := pkg .Open (path )
87
98
if err != nil {
88
99
return err
89
100
}
@@ -102,12 +113,10 @@ func (d *DerivationBuilder) copyFile(pkgFS fs.FS, path string) error {
102
113
perm = fs .FileMode (0o777 )
103
114
}
104
115
105
- osPath , err := filepath . Localize (path )
116
+ dstPath , err := out . OSPath (path )
106
117
if err != nil {
107
118
return err
108
119
}
109
- dstPath := filepath .Join (d .Out , osPath )
110
-
111
120
dst , err := os .OpenFile (dstPath , os .O_CREATE | os .O_WRONLY | os .O_EXCL , perm )
112
121
if err != nil {
113
122
return err
@@ -121,22 +130,53 @@ func (d *DerivationBuilder) copyFile(pkgFS fs.FS, path string) error {
121
130
return dst .Close ()
122
131
}
123
132
124
- func (d * DerivationBuilder ) copySymlink (pkgStorePath , path string ) error {
125
- // The fs package doesn't support symlinks, so we need to convert the
126
- // path back to an OS path to see what it points to.
127
- osPath , err := filepath .Localize (path )
133
+ func (d * DerivationBuilder ) copySymlink (pkg , out * packageFS , path string ) error {
134
+ link , err := out .OSPath (path )
128
135
if err != nil {
129
136
return err
130
137
}
131
- target , err := os .Readlink (filepath . Join ( pkgStorePath , osPath ) )
138
+ target , err := pkg .Readlink (path )
132
139
if err != nil {
133
140
return err
134
141
}
135
- // TODO(gcurtis): translate absolute symlink targets to relative paths.
136
- return os .Symlink (target , filepath .Join (d .Out , osPath ))
142
+ return os .Symlink (target , link )
143
+ }
144
+
145
+ // packageFS is the tree of files for a package in the Nix store.
146
+ type packageFS struct {
147
+ fs.FS
148
+ storePath string
149
+ }
150
+
151
+ // newPackageFS returns a packageFS for the given store path.
152
+ func newPackageFS (storePath string ) * packageFS {
153
+ return & packageFS {
154
+ FS : os .DirFS (storePath ),
155
+ storePath : storePath ,
156
+ }
157
+ }
158
+
159
+ // Readlink returns the destination of a symlink.
160
+ func (p * packageFS ) Readlink (path string ) (string , error ) {
161
+ osPath , err := p .OSPath (path )
162
+ if err != nil {
163
+ return "" , err
164
+ }
165
+ // TODO(gcurtis): check that the symlink isn't absolute or points
166
+ // outside the Nix store.
167
+ return os .Readlink (osPath )
168
+ }
169
+
170
+ // OSPath translates a package-relative path to an operating system path.
171
+ func (p * packageFS ) OSPath (path string ) (string , error ) {
172
+ local , err := filepath .Localize (path )
173
+ if err != nil {
174
+ return "" , err
175
+ }
176
+ return filepath .Join (p .storePath , local ), nil
137
177
}
138
178
139
- // RegularFiles iterates over all files in fsys starting at root. It silently
179
+ // allFiles iterates over all files in fsys starting at root. It silently
140
180
// ignores errors.
141
181
func allFiles (fsys fs.FS , root string ) iter.Seq2 [string , fs.DirEntry ] {
142
182
return func (yield func (string , fs.DirEntry ) bool ) {
0 commit comments