@@ -48,18 +48,29 @@ func (d *DerivationBuilder) init() error {
4848// Build applies patches to a package store path and puts the result in the
4949// d.Out directory.
5050func (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+ }
5254
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 {
5361 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+
5667 switch {
5768 case entry .IsDir ():
58- err = d .copyDir (path )
69+ err = d .copyDir (out , path )
5970 case isSymlink (entry .Type ()):
60- err = d .copySymlink (pkgStorePath , path )
71+ err = d .copySymlink (pkg , out , path )
6172 default :
62- err = d .copyFile (pkgFS , path )
73+ err = d .copyFile (pkg , out , path )
6374 }
6475
6576 if err != nil {
@@ -74,16 +85,16 @@ func (d *DerivationBuilder) Build(ctx context.Context, pkgStorePath string) erro
7485 return cmd .Run ()
7586}
7687
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 )
7990 if err != nil {
8091 return err
8192 }
82- return os .Mkdir (filepath . Join ( d . Out , osPath ) , 0o777 )
93+ return os .Mkdir (path , 0o777 )
8394}
8495
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 )
8798 if err != nil {
8899 return err
89100 }
@@ -102,12 +113,10 @@ func (d *DerivationBuilder) copyFile(pkgFS fs.FS, path string) error {
102113 perm = fs .FileMode (0o777 )
103114 }
104115
105- osPath , err := filepath . Localize (path )
116+ dstPath , err := out . OSPath (path )
106117 if err != nil {
107118 return err
108119 }
109- dstPath := filepath .Join (d .Out , osPath )
110-
111120 dst , err := os .OpenFile (dstPath , os .O_CREATE | os .O_WRONLY | os .O_EXCL , perm )
112121 if err != nil {
113122 return err
@@ -121,22 +130,53 @@ func (d *DerivationBuilder) copyFile(pkgFS fs.FS, path string) error {
121130 return dst .Close ()
122131}
123132
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 )
128135 if err != nil {
129136 return err
130137 }
131- target , err := os .Readlink (filepath . Join ( pkgStorePath , osPath ) )
138+ target , err := pkg .Readlink (path )
132139 if err != nil {
133140 return err
134141 }
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
137177}
138178
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
140180// ignores errors.
141181func allFiles (fsys fs.FS , root string ) iter.Seq2 [string , fs.DirEntry ] {
142182 return func (yield func (string , fs.DirEntry ) bool ) {
0 commit comments