@@ -10,6 +10,7 @@ import (
1010 "path/filepath"
1111 "slices"
1212 "strings"
13+ "unicode"
1314
1415 "github.com/gim-home/azldev-preview/internal/app/azldev/core/components"
1516 "github.com/gim-home/azldev-preview/internal/global/opctx"
@@ -91,36 +92,37 @@ func (p *sourcePreparerImpl) PrepareSources(
9192 component .GetName (), err )
9293 }
9394
94- // Apply any postprocessing to the sources in-place, in the output directory.
95- err = p .postProcessSources (component , outputDir )
96- if err != nil {
97- return fmt .Errorf ("failed to post-process sources for component %q:\n %w" , component .GetName (), err )
98- }
99-
10095 // Emit computed macros to a macros file in the output directory.
101- err = p .writeMacrosFile (component , outputDir )
96+ macrosFilePath , err : = p .writeMacrosFile (component , outputDir )
10297 if err != nil {
10398 return fmt .Errorf ("failed to write macros file for component %#q:\n %w" ,
10499 component .GetName (), err )
105100 }
106101
102+ // Apply any postprocessing to the sources in-place, in the output directory.
103+ err = p .postProcessSources (component , outputDir , filepath .Base (macrosFilePath ))
104+ if err != nil {
105+ return fmt .Errorf ("failed to post-process sources for component %q:\n %w" , component .GetName (), err )
106+ }
107+
107108 return nil
108109}
109110
110111// writeMacrosFile writes a macros file containing the resolved macros for a component.
111112// This includes with/without flags converted to macro format, and any explicit defines.
112- // The file is always created, even if empty (aside from the header comment).
113- func (p * sourcePreparerImpl ) writeMacrosFile (component components.Component , outputDir string ) error {
113+ // The file is always created, even if empty (aside from the header comment). Returns
114+ // the path to the written macros file, which is guaranteed to be within the given outputDir.
115+ func (p * sourcePreparerImpl ) writeMacrosFile (component components.Component , outputDir string ) (string , error ) {
114116 contents := GenerateMacrosFileContents (component .GetConfig ().Build )
115117
116118 macrosFilePath := filepath .Join (outputDir , component .GetName ()+ MacrosFileExtension )
117119
118120 err := fileutils .WriteFile (p .fs , macrosFilePath , []byte (contents ), fileperms .PublicFile )
119121 if err != nil {
120- return fmt .Errorf ("failed to write macros file %#q:\n %w" , macrosFilePath , err )
122+ return "" , fmt .Errorf ("failed to write macros file %#q:\n %w" , macrosFilePath , err )
121123 }
122124
123- return nil
125+ return macrosFilePath , nil
124126}
125127
126128// GenerateMacrosFileContents generates the contents of an RPM macros file from the given
@@ -174,16 +176,12 @@ func GenerateMacrosFileContents(buildConfig projectconfig.ComponentBuildConfig)
174176}
175177
176178func (p * sourcePreparerImpl ) postProcessSources (
177- component components.Component , sourcesDirPath string ,
179+ component components.Component , sourcesDirPath , macrosFileName string ,
178180) error {
179- // Short-circuit early if no overlays to apply.
180- if len (component .GetConfig ().Overlays ) == 0 {
181- return nil
182- }
183-
184181 event := p .eventListener .StartEvent ("Applying overlays to sources" , "component" , component .GetName ())
185182 defer event .End ()
186183
184+ // Find the spec.
187185 specPath , err := findSpecInDir (p .fs , component , sourcesDirPath )
188186 if err != nil {
189187 return fmt .Errorf ("failed to find spec in acquired sources dir %#q:\n %w" , sourcesDirPath , err )
@@ -195,6 +193,21 @@ func (p *sourcePreparerImpl) postProcessSources(
195193 return fmt .Errorf ("failed to get absolute path for %#q:\n %w" , specPath , err )
196194 }
197195
196+ // Compute any synthetic overlays required to load the macros file.
197+ macroOverlays , err := synthesizeMacroLoadOverlays (macrosFileName )
198+ if err != nil {
199+ return fmt .Errorf ("failed to compute macros load overlays:\n %w" , err )
200+ }
201+
202+ // Apply those overlays *first*, in sequence.
203+ for _ , overlay := range macroOverlays {
204+ err = ApplyOverlayToSources (p .dryRunnable , p .fs , overlay , sourcesDirPath , absSpecPath )
205+ if err != nil {
206+ return fmt .Errorf ("failed to apply system overlay to sources for component %#q:\n %w" , component .GetName (), err )
207+ }
208+ }
209+
210+ // Apply all overlays in sequence.
198211 for _ , overlay := range component .GetConfig ().Overlays {
199212 err = ApplyOverlayToSources (p .dryRunnable , p .fs , overlay , sourcesDirPath , absSpecPath )
200213 if err != nil {
@@ -205,6 +218,39 @@ func (p *sourcePreparerImpl) postProcessSources(
205218 return nil
206219}
207220
221+ func synthesizeMacroLoadOverlays (macrosFileName string ) ([]projectconfig.ComponentOverlay , error ) {
222+ // Basic check that the macros file name is valid and doesn't require escaping.
223+ if strings .ContainsFunc (macrosFileName , func (r rune ) bool {
224+ return ! unicode .IsLetter (r ) && ! unicode .IsDigit (r ) && r != '.' && r != '-' && r != '_'
225+ }) {
226+ return nil , fmt .Errorf (
227+ "macros file name %#q contains invalid characters; does the component name contain invalid characters?" ,
228+ macrosFileName ,
229+ )
230+ }
231+
232+ // We inject an overlay to prepend a line to the spec to load the macros file.
233+ return []projectconfig.ComponentOverlay {
234+ {
235+ // Prepend the %{load:...} directive to the spec.
236+ Type : projectconfig .ComponentOverlayPrependSpecLines ,
237+ Lines : []string {
238+ fmt .Sprintf ("%%{load:%%{_sourcedir}/%s}" , macrosFileName ),
239+ },
240+ },
241+ {
242+ // Ensure that the macros file is manifested as a source in the spec so that
243+ // mock and other tools know it needs to be present in the build root.
244+ // Ideally we'd dynamically compute a unique source tag here, but for now
245+ // we just use a high number to stay simple. If a conflict is found, this
246+ // overlay application will fail.
247+ Type : projectconfig .ComponentOverlayAddSpecTag ,
248+ Tag : "Source9999" , // Use a high number to avoid conflicts with existing sources.
249+ Value : macrosFileName ,
250+ },
251+ }, nil
252+ }
253+
208254func findSpecInDir (
209255 fs opctx.FS , component components.Component , dirPath string ,
210256) (string , error ) {
0 commit comments