Skip to content

Commit 0f08993

Browse files
author
Bryan C. Mills
committed
modfile: add SetRequireSeparateIndirect
The new method is a variant of SetRequire, but adds new indirect dependencies only in indirect-only blocks, and does not add new direct dependencies to existing indirect-only blocks. For golang/go#45965 Change-Id: I6730b586396658e710e4bf2afcf64fb2c827203f Reviewed-on: https://go-review.googlesource.com/c/mod/+/325971 Trust: Bryan C. Mills <bcmills@google.com> Run-TryBot: Bryan C. Mills <bcmills@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org>
1 parent 22458ad commit 0f08993

File tree

2 files changed

+492
-0
lines changed

2 files changed

+492
-0
lines changed

modfile/rule.go

+169
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,175 @@ func (f *File) SetRequire(req []*Require) {
954954
f.SortBlocks()
955955
}
956956

957+
// SetRequireSeparateIndirect updates the requirements of f to contain the given
958+
// requirements. Comment contents (except for 'indirect' markings) are retained
959+
// from the first existing requirement for each module path, and block structure
960+
// is maintained as long as the indirect markings match.
961+
//
962+
// Any requirements on paths not already present in the file are added. Direct
963+
// requirements are added to the last block containing *any* other direct
964+
// requirement. Indirect requirements are added to the last block containing
965+
// *only* other indirect requirements. If no suitable block exists, a new one is
966+
// added, with the last block containing a direct dependency (if any)
967+
// immediately before the first block containing only indirect dependencies.
968+
//
969+
// The Syntax field is ignored for requirements in the given blocks.
970+
func (f *File) SetRequireSeparateIndirect(req []*Require) {
971+
type modKey struct {
972+
path string
973+
indirect bool
974+
}
975+
need := make(map[modKey]string)
976+
for _, r := range req {
977+
need[modKey{r.Mod.Path, r.Indirect}] = r.Mod.Version
978+
}
979+
980+
comments := make(map[string]Comments)
981+
for _, r := range f.Require {
982+
v, ok := need[modKey{r.Mod.Path, r.Indirect}]
983+
if !ok {
984+
if _, ok := need[modKey{r.Mod.Path, !r.Indirect}]; ok {
985+
if _, dup := comments[r.Mod.Path]; !dup {
986+
comments[r.Mod.Path] = r.Syntax.Comments
987+
}
988+
}
989+
r.markRemoved()
990+
continue
991+
}
992+
r.setVersion(v)
993+
delete(need, modKey{r.Mod.Path, r.Indirect})
994+
}
995+
996+
var (
997+
lastDirectOrMixedBlock Expr
998+
firstIndirectOnlyBlock Expr
999+
lastIndirectOnlyBlock Expr
1000+
)
1001+
for _, stmt := range f.Syntax.Stmt {
1002+
switch stmt := stmt.(type) {
1003+
case *Line:
1004+
if len(stmt.Token) == 0 || stmt.Token[0] != "require" {
1005+
continue
1006+
}
1007+
if isIndirect(stmt) {
1008+
lastIndirectOnlyBlock = stmt
1009+
} else {
1010+
lastDirectOrMixedBlock = stmt
1011+
}
1012+
case *LineBlock:
1013+
if len(stmt.Token) == 0 || stmt.Token[0] != "require" {
1014+
continue
1015+
}
1016+
indirectOnly := true
1017+
for _, line := range stmt.Line {
1018+
if len(line.Token) == 0 {
1019+
continue
1020+
}
1021+
if !isIndirect(line) {
1022+
indirectOnly = false
1023+
break
1024+
}
1025+
}
1026+
if indirectOnly {
1027+
lastIndirectOnlyBlock = stmt
1028+
if firstIndirectOnlyBlock == nil {
1029+
firstIndirectOnlyBlock = stmt
1030+
}
1031+
} else {
1032+
lastDirectOrMixedBlock = stmt
1033+
}
1034+
}
1035+
}
1036+
1037+
isOrContainsStmt := func(stmt Expr, target Expr) bool {
1038+
if stmt == target {
1039+
return true
1040+
}
1041+
if stmt, ok := stmt.(*LineBlock); ok {
1042+
if target, ok := target.(*Line); ok {
1043+
for _, line := range stmt.Line {
1044+
if line == target {
1045+
return true
1046+
}
1047+
}
1048+
}
1049+
}
1050+
return false
1051+
}
1052+
1053+
addRequire := func(path, vers string, indirect bool, comments Comments) {
1054+
var line *Line
1055+
if indirect {
1056+
if lastIndirectOnlyBlock != nil {
1057+
line = f.Syntax.addLine(lastIndirectOnlyBlock, "require", path, vers)
1058+
} else {
1059+
// Add a new require block after the last direct-only or mixed "require"
1060+
// block (if any).
1061+
//
1062+
// (f.Syntax.addLine would add the line to an existing "require" block if
1063+
// present, but here the existing "require" blocks are all direct-only, so
1064+
// we know we need to add a new block instead.)
1065+
line = &Line{Token: []string{"require", path, vers}}
1066+
lastIndirectOnlyBlock = line
1067+
firstIndirectOnlyBlock = line // only block implies first block
1068+
if lastDirectOrMixedBlock == nil {
1069+
f.Syntax.Stmt = append(f.Syntax.Stmt, line)
1070+
} else {
1071+
for i, stmt := range f.Syntax.Stmt {
1072+
if isOrContainsStmt(stmt, lastDirectOrMixedBlock) {
1073+
f.Syntax.Stmt = append(f.Syntax.Stmt, nil) // increase size
1074+
copy(f.Syntax.Stmt[i+2:], f.Syntax.Stmt[i+1:]) // shuffle elements up
1075+
f.Syntax.Stmt[i+1] = line
1076+
break
1077+
}
1078+
}
1079+
}
1080+
}
1081+
} else {
1082+
if lastDirectOrMixedBlock != nil {
1083+
line = f.Syntax.addLine(lastDirectOrMixedBlock, "require", path, vers)
1084+
} else {
1085+
// Add a new require block before the first indirect block (if any).
1086+
//
1087+
// That way if the file initially contains only indirect lines,
1088+
// the direct lines still appear before it: we preserve existing
1089+
// structure, but only to the extent that that structure already
1090+
// reflects the direct/indirect split.
1091+
line = &Line{Token: []string{"require", path, vers}}
1092+
lastDirectOrMixedBlock = line
1093+
if firstIndirectOnlyBlock == nil {
1094+
f.Syntax.Stmt = append(f.Syntax.Stmt, line)
1095+
} else {
1096+
for i, stmt := range f.Syntax.Stmt {
1097+
if isOrContainsStmt(stmt, firstIndirectOnlyBlock) {
1098+
f.Syntax.Stmt = append(f.Syntax.Stmt, nil) // increase size
1099+
copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:]) // shuffle elements up
1100+
f.Syntax.Stmt[i] = line
1101+
break
1102+
}
1103+
}
1104+
}
1105+
}
1106+
}
1107+
1108+
line.Comments.Before = commentsAdd(line.Comments.Before, comments.Before)
1109+
line.Comments.Suffix = commentsAdd(line.Comments.Suffix, comments.Suffix)
1110+
1111+
r := &Require{
1112+
Mod: module.Version{Path: path, Version: vers},
1113+
Indirect: indirect,
1114+
Syntax: line,
1115+
}
1116+
r.setIndirect(indirect)
1117+
f.Require = append(f.Require, r)
1118+
}
1119+
1120+
for k, vers := range need {
1121+
addRequire(k.path, vers, k.indirect, comments[k.path])
1122+
}
1123+
f.SortBlocks()
1124+
}
1125+
9571126
func (f *File) DropRequire(path string) error {
9581127
for _, r := range f.Require {
9591128
if r.Mod.Path == path {

0 commit comments

Comments
 (0)