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