Skip to content

Commit 778df82

Browse files
fix(java): correctly inherit version and scope from upper/root depManagement and dependencies into parents (#7541)
Signed-off-by: knqyf263 <[email protected]> Co-authored-by: knqyf263 <[email protected]>
1 parent c8c14d3 commit 778df82

File tree

20 files changed

+1013
-84
lines changed

20 files changed

+1013
-84
lines changed

pkg/dependency/parser/java/pom/parse.go

+74-73
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func NewParser(filePath string, opts ...option) *Parser {
9696
}
9797

9898
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
99-
content, err := parsePom(r)
99+
content, err := parsePom(r, true)
100100
if err != nil {
101101
return nil, nil, xerrors.Errorf("failed to parse POM: %w", err)
102102
}
@@ -107,7 +107,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc
107107
}
108108

109109
// Analyze root POM
110-
result, err := p.analyze(root, analysisOptions{lineNumber: true})
110+
result, err := p.analyze(root, analysisOptions{})
111111
if err != nil {
112112
return nil, nil, xerrors.Errorf("analyze error (%s): %w", p.rootPath, err)
113113
}
@@ -330,53 +330,27 @@ type analysisResult struct {
330330
type analysisOptions struct {
331331
exclusions map[string]struct{}
332332
depManagement []pomDependency // from the root POM
333-
lineNumber bool // Save line numbers
334333
}
335334

336335
func (p *Parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error) {
337-
if pom == nil || pom.content == nil {
336+
if pom.nil() {
338337
return analysisResult{}, nil
339338
}
340-
341339
// Update remoteRepositories
342340
pomReleaseRemoteRepos, pomSnapshotRemoteRepos := pom.repositories(p.servers)
343341
p.releaseRemoteRepos = lo.Uniq(append(pomReleaseRemoteRepos, p.releaseRemoteRepos...))
344342
p.snapshotRemoteRepos = lo.Uniq(append(pomSnapshotRemoteRepos, p.snapshotRemoteRepos...))
345343

346-
// We need to forward dependencyManagements from current and root pom to Parent,
347-
// to use them for dependencies in parent.
348-
// For better understanding see the following tests:
349-
// - `dependency from parent uses version from child pom depManagement`
350-
// - `dependency from parent uses version from root pom depManagement`
351-
//
352-
// depManagements from root pom has higher priority than depManagements from current pom.
353-
depManagementForParent := lo.UniqBy(append(opts.depManagement, pom.content.DependencyManagement.Dependencies.Dependency...),
354-
func(dep pomDependency) string {
355-
return dep.Name()
356-
})
357-
358-
// Parent
359-
parent, err := p.parseParent(pom.filePath, pom.content.Parent, depManagementForParent)
360-
if err != nil {
361-
return analysisResult{}, xerrors.Errorf("parent error: %w", err)
344+
// Resolve parent POM
345+
if err := p.resolveParent(pom); err != nil {
346+
return analysisResult{}, xerrors.Errorf("pom resolve error: %w", err)
362347
}
363348

364-
// Inherit values/properties from parent
365-
pom.inherit(parent)
366-
367-
// Generate properties
349+
// Resolve dependencies
368350
props := pom.properties()
369-
370-
// dependencyManagements have the next priority:
371-
// 1. Managed dependencies from this POM
372-
// 2. Managed dependencies from parent of this POM
373-
depManagement := p.mergeDependencyManagements(pom.content.DependencyManagement.Dependencies.Dependency,
374-
parent.dependencyManagement)
375-
376-
// Merge dependencies. Child dependencies must be preferred than parent dependencies.
377-
// Parents don't have to resolve dependencies.
351+
depManagement := pom.content.DependencyManagement.Dependencies.Dependency
378352
deps := p.parseDependencies(pom.content.Dependencies.Dependency, props, depManagement, opts)
379-
deps = p.mergeDependencies(parent.dependencies, deps, opts.exclusions)
353+
deps = p.filterDependencies(deps, opts.exclusions)
380354

381355
return analysisResult{
382356
filePath: pom.filePath,
@@ -388,6 +362,39 @@ func (p *Parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error)
388362
}, nil
389363
}
390364

365+
// resolveParent resolves its parent POMs and inherits properties, dependencies, and dependencyManagement.
366+
func (p *Parser) resolveParent(pom *pom) error {
367+
if pom.nil() {
368+
return nil
369+
}
370+
371+
// Parse parent POM
372+
parent, err := p.parseParent(pom.filePath, pom.content.Parent)
373+
if err != nil {
374+
return xerrors.Errorf("parent error: %w", err)
375+
}
376+
377+
// Inherit values/properties from parent
378+
pom.inherit(parent)
379+
380+
// Merge properties
381+
pom.content.Properties = p.mergeProperties(pom.content.Properties, parent.content.Properties)
382+
383+
// Merge dependencyManagement with the following priority:
384+
// 1. Managed dependencies from this POM
385+
// 2. Managed dependencies from parent of this POM
386+
pom.content.DependencyManagement.Dependencies.Dependency = p.mergeDependencyManagements(
387+
pom.content.DependencyManagement.Dependencies.Dependency,
388+
parent.content.DependencyManagement.Dependencies.Dependency)
389+
390+
// Merge dependencies
391+
pom.content.Dependencies.Dependency = p.mergeDependencies(
392+
pom.content.Dependencies.Dependency,
393+
parent.content.Dependencies.Dependency)
394+
395+
return nil
396+
}
397+
391398
func (p *Parser) mergeDependencyManagements(depManagements ...[]pomDependency) []pomDependency {
392399
uniq := make(map[string]struct{})
393400
var depManagement []pomDependency
@@ -463,22 +470,20 @@ func (p *Parser) resolveDepManagement(props map[string]string, depManagement []p
463470
return newDepManagement
464471
}
465472

466-
func (p *Parser) mergeDependencies(parent, child []artifact, exclusions map[string]struct{}) []artifact {
467-
var deps []artifact
468-
unique := make(map[string]struct{})
473+
func (p *Parser) mergeProperties(child, parent properties) properties {
474+
return lo.Assign(parent, child)
475+
}
469476

470-
for _, d := range append(child, parent...) {
471-
if excludeDep(exclusions, d) {
472-
continue
473-
}
474-
if _, ok := unique[d.Name()]; ok {
475-
continue
476-
}
477-
unique[d.Name()] = struct{}{}
478-
deps = append(deps, d)
479-
}
477+
func (p *Parser) mergeDependencies(child, parent []pomDependency) []pomDependency {
478+
return lo.UniqBy(append(child, parent...), func(d pomDependency) string {
479+
return d.Name()
480+
})
481+
}
480482

481-
return deps
483+
func (p *Parser) filterDependencies(artifacts []artifact, exclusions map[string]struct{}) []artifact {
484+
return lo.Filter(artifacts, func(art artifact, _ int) bool {
485+
return !excludeDep(exclusions, art)
486+
})
482487
}
483488

484489
func excludeDep(exclusions map[string]struct{}, art artifact) bool {
@@ -497,38 +502,29 @@ func excludeDep(exclusions map[string]struct{}, art artifact) bool {
497502
return false
498503
}
499504

500-
func (p *Parser) parseParent(currentPath string, parent pomParent, rootDepManagement []pomDependency) (analysisResult, error) {
505+
func (p *Parser) parseParent(currentPath string, parent pomParent) (*pom, error) {
501506
// Pass nil properties so that variables in <parent> are not evaluated.
502507
target := newArtifact(parent.GroupId, parent.ArtifactId, parent.Version, nil, nil)
503508
// if version is property (e.g. ${revision}) - we still need to parse this pom
504509
if target.IsEmpty() && !isProperty(parent.Version) {
505-
return analysisResult{}, nil
510+
return &pom{content: &pomXML{}}, nil
506511
}
507512

508513
logger := p.logger.With("artifact", target.String())
509514
logger.Debug("Start parent")
510515
defer logger.Debug("Exit parent")
511516

512-
// If the artifact is found in cache, it is returned.
513-
if result := p.cache.get(target); result != nil {
514-
return *result, nil
515-
}
516-
517517
parentPOM, err := p.retrieveParent(currentPath, parent.RelativePath, target)
518518
if err != nil {
519519
logger.Debug("Parent POM not found", log.Err(err))
520+
return &pom{content: &pomXML{}}, nil
520521
}
521522

522-
result, err := p.analyze(parentPOM, analysisOptions{
523-
depManagement: rootDepManagement,
524-
})
525-
if err != nil {
526-
return analysisResult{}, xerrors.Errorf("analyze error: %w", err)
523+
if err = p.resolveParent(parentPOM); err != nil {
524+
return nil, xerrors.Errorf("parent pom resolve error: %w", err)
527525
}
528526

529-
p.cache.put(target, result)
530-
531-
return result, nil
527+
return parentPOM, nil
532528
}
533529

534530
func (p *Parser) retrieveParent(currentPath, relativePath string, target artifact) (*pom, error) {
@@ -565,7 +561,7 @@ func (p *Parser) retrieveParent(currentPath, relativePath string, target artifac
565561
}
566562

567563
func (p *Parser) tryRelativePath(parentArtifact artifact, currentPath, relativePath string) (*pom, error) {
568-
pom, err := p.openRelativePom(currentPath, relativePath)
564+
parsedPOM, err := p.openRelativePom(currentPath, relativePath)
569565
if err != nil {
570566
return nil, err
571567
}
@@ -576,19 +572,18 @@ func (p *Parser) tryRelativePath(parentArtifact artifact, currentPath, relativeP
576572
// But GroupID can be inherited from parent (`p.analyze` function is required to get the GroupID).
577573
// Version can contain a property (`p.analyze` function is required to get the GroupID).
578574
// So we can only match ArtifactID's.
579-
if pom.artifact().ArtifactID != parentArtifact.ArtifactID {
575+
if parsedPOM.artifact().ArtifactID != parentArtifact.ArtifactID {
580576
return nil, xerrors.New("'parent.relativePath' points at wrong local POM")
581577
}
582-
result, err := p.analyze(pom, analysisOptions{})
583-
if err != nil {
578+
if err := p.resolveParent(parsedPOM); err != nil {
584579
return nil, xerrors.Errorf("analyze error: %w", err)
585580
}
586581

587-
if !parentArtifact.Equal(result.artifact) {
582+
if !parentArtifact.Equal(parsedPOM.artifact()) {
588583
return nil, xerrors.New("'parent.relativePath' points at wrong local POM")
589584
}
590585

591-
return pom, nil
586+
return parsedPOM, nil
592587
}
593588

594589
func (p *Parser) openRelativePom(currentPath, relativePath string) (*pom, error) {
@@ -620,7 +615,7 @@ func (p *Parser) openPom(filePath string) (*pom, error) {
620615
}
621616
defer f.Close()
622617

623-
content, err := parsePom(f)
618+
content, err := parsePom(f, false)
624619
if err != nil {
625620
return nil, xerrors.Errorf("failed to parse the local POM: %w", err)
626621
}
@@ -777,7 +772,7 @@ func (p *Parser) fetchPOMFromRemoteRepository(repo string, paths []string) (*pom
777772
}
778773
defer resp.Body.Close()
779774

780-
content, err := parsePom(resp.Body)
775+
content, err := parsePom(resp.Body, false)
781776
if err != nil {
782777
return nil, xerrors.Errorf("failed to parse the remote POM: %w", err)
783778
}
@@ -788,13 +783,19 @@ func (p *Parser) fetchPOMFromRemoteRepository(repo string, paths []string) (*pom
788783
}, nil
789784
}
790785

791-
func parsePom(r io.Reader) (*pomXML, error) {
786+
func parsePom(r io.Reader, lineNumber bool) (*pomXML, error) {
792787
parsed := &pomXML{}
793788
decoder := xml.NewDecoder(r)
794789
decoder.CharsetReader = charset.NewReaderLabel
795790
if err := decoder.Decode(parsed); err != nil {
796791
return nil, xerrors.Errorf("xml decode error: %w", err)
797792
}
793+
if !lineNumber {
794+
for i := range parsed.Dependencies.Dependency {
795+
parsed.Dependencies.Dependency[i].StartLine = 0
796+
parsed.Dependencies.Dependency[i].EndLine = 0
797+
}
798+
}
798799
return parsed, nil
799800
}
800801

0 commit comments

Comments
 (0)