@@ -187,6 +187,31 @@ func tagFamily(tag string) string {
187187 return lower [:end ]
188188}
189189
190+ // conditionalDepthChange returns +1 for lines that open a conditional block (%if, %ifarch, etc.),
191+ // -1 for %endif, and 0 for everything else. Comments are ignored.
192+ func conditionalDepthChange (rawLine string ) int {
193+ trimmed := strings .TrimSpace (rawLine )
194+ if strings .HasPrefix (trimmed , "#" ) {
195+ return 0
196+ }
197+
198+ token := strings .Fields (trimmed )
199+ if len (token ) == 0 {
200+ return 0
201+ }
202+
203+ lower := strings .ToLower (token [0 ])
204+ if lower == "%endif" {
205+ return - 1
206+ }
207+
208+ if strings .HasPrefix (lower , "%if" ) {
209+ return 1
210+ }
211+
212+ return 0
213+ }
214+
190215// InsertTag inserts a tag into the spec, placing it after the last existing tag from the
191216// same "family" (e.g., Source9999 is placed after the last Source* tag). If no tags from
192217// the same family exist, the tag is placed after the last tag of any kind. If there are no
@@ -196,6 +221,9 @@ func tagFamily(tag string) string {
196221// (case-insensitive). For example, "Source0", "Source1", and "Source" all belong to the
197222// "source" family.
198223//
224+ // If the chosen insertion point falls inside a conditional block (%if/%endif), the tag is
225+ // placed after the closing %endif instead, so it remains unconditional.
226+ //
199227// Note: When inserting into a sub-package (non-empty packageName), the corresponding
200228// %package section must already exist in the spec; otherwise, an [ErrSectionNotFound]
201229// error is returned.
@@ -205,30 +233,75 @@ func (s *Spec) InsertTag(packageName string, tag string, value string) error {
205233 family := tagFamily (tag )
206234 newLine := fmt .Sprintf ("%s: %s" , tag , value )
207235
208- // First pass: find the best insertion point.
209- // Track both the last same-family tag line and the last tag of any kind.
210- lastFamilyTagLineNum := - 1
211- lastAnyTagLineNum := - 1
212- sectionFound := false
213-
214236 sectionName := ""
215237 if packageName != "" {
216238 sectionName = "%package"
217239 }
218240
241+ result , err := s .findInsertTagPosition (sectionName , packageName , family )
242+ if err != nil {
243+ return err
244+ }
245+
246+ // Determine insertion point: prefer same-family, then any tag, then fall back to AddTag.
247+ insertAfterLine := result .lastFamilyTagLineNum
248+ if insertAfterLine < 0 {
249+ insertAfterLine = result .lastAnyTagLineNum
250+ }
251+
252+ if insertAfterLine < 0 {
253+ // No tags at all — fall back to AddTag behavior.
254+ return s .AddTag (packageName , tag , value )
255+ }
256+
257+ // If the insertion point is inside a conditional block, move it forward past the
258+ // closing %endif so the new tag doesn't become conditional.
259+ insertAfterLine = s .skipPastConditional (insertAfterLine , result .sectionEndLineNum )
260+
261+ // Insert after the found line (0-indexed, so insertAfterLine+1).
262+ s .InsertLinesAt ([]string {newLine }, insertAfterLine + 1 )
263+
264+ return nil
265+ }
266+
267+ // insertTagScanResult holds the results of scanning a spec for a tag insertion point.
268+ type insertTagScanResult struct {
269+ lastFamilyTagLineNum int
270+ lastAnyTagLineNum int
271+ sectionEndLineNum int
272+ }
273+
274+ // findInsertTagPosition scans the spec to find the best insertion point for a tag of the
275+ // given family within the specified section/package. Returns the scan results or an error
276+ // if the target section is not found.
277+ func (s * Spec ) findInsertTagPosition (
278+ sectionName , packageName , family string ,
279+ ) (insertTagScanResult , error ) {
280+ result := insertTagScanResult {
281+ lastFamilyTagLineNum : - 1 ,
282+ lastAnyTagLineNum : - 1 ,
283+ sectionEndLineNum : len (s .rawLines ),
284+ }
285+
286+ sectionFound := false
287+
219288 err := s .Visit (func (ctx * Context ) error {
220- // Track whether the target section exists.
221289 if ctx .Target .TargetType == SectionStartTarget {
222290 if ctx .CurrentSection .SectName == sectionName && ctx .CurrentSection .Package == packageName {
223291 sectionFound = true
224292 }
225293 }
226294
295+ if ctx .Target .TargetType == SectionEndTarget {
296+ if ctx .CurrentSection .SectName == sectionName && ctx .CurrentSection .Package == packageName {
297+ result .sectionEndLineNum = ctx .CurrentLineNum
298+ }
299+ }
300+
227301 if ctx .Target .TargetType != SectionLineTarget {
228302 return nil
229303 }
230304
231- // Only consider tags in the target section/package.
232305 if ctx .CurrentSection .SectName != sectionName || ctx .CurrentSection .Package != packageName {
233306 return nil
234307 }
@@ -242,37 +315,50 @@ func (s *Spec) InsertTag(packageName string, tag string, value string) error {
242315 return nil
243316 }
244317
245- lastAnyTagLineNum = ctx .CurrentLineNum
318+ result . lastAnyTagLineNum = ctx .CurrentLineNum
246319
247320 if tagFamily (tagLine .Tag ) == family {
248- lastFamilyTagLineNum = ctx .CurrentLineNum
321+ result . lastFamilyTagLineNum = ctx .CurrentLineNum
249322 }
250323
251324 return nil
252325 })
253326 if err != nil {
254- return fmt .Errorf ("failed to scan spec for tag insertion point:\n %w" , err )
327+ return result , fmt .Errorf ("failed to scan spec for tag insertion point:\n %w" , err )
255328 }
256329
257330 if ! sectionFound {
258- return fmt .Errorf ("section %#q (package=%#q) not found:\n %w" , sectionName , packageName , ErrSectionNotFound )
331+ return result , fmt .Errorf ("section %#q (package=%#q) not found:\n %w" , sectionName , packageName , ErrSectionNotFound )
259332 }
260333
261- // Determine insertion point: prefer same-family, then any tag, then fall back to AddTag.
262- insertAfterLine := lastFamilyTagLineNum
263- if insertAfterLine < 0 {
264- insertAfterLine = lastAnyTagLineNum
334+ return result , nil
335+ }
336+
337+ // skipPastConditional checks whether lineNum falls inside a conditional block by computing
338+ // the conditional nesting depth from the start of the file up to that line. If depth > 0,
339+ // it scans forward to find the %endif that brings depth back to 0 and returns that line
340+ // number. Otherwise it returns lineNum unchanged.
341+ func (s * Spec ) skipPastConditional (lineNum int , sectionEnd int ) int {
342+ // Compute conditional depth at the insertion point by scanning from the start.
343+ depth := 0
344+ for i := 0 ; i <= lineNum && i < len (s .rawLines ); i ++ {
345+ depth += conditionalDepthChange (s .rawLines [i ])
265346 }
266347
267- if insertAfterLine < 0 {
268- // No tags at all — fall back to AddTag behavior.
269- return s .AddTag (packageName , tag , value )
348+ if depth <= 0 {
349+ return lineNum
270350 }
271351
272- // Insert after the found line (0-indexed, so insertAfterLine+1).
273- s .InsertLinesAt ([]string {newLine }, insertAfterLine + 1 )
352+ // Scan forward to find the %endif that closes the conditional.
353+ for i := lineNum + 1 ; i < sectionEnd && i < len (s .rawLines ); i ++ {
354+ depth += conditionalDepthChange (s .rawLines [i ])
355+ if depth <= 0 {
356+ return i
357+ }
358+ }
274359
275- return nil
360+ // Could not find a closing %endif within the section; return the original position.
361+ return lineNum
276362}
277363
278364// PrependLinesToSection prepends the given lines to the start of the specified section, placing
0 commit comments