// Package srcinfo is a parser for srcinfo files. Typically generated by // makepkg, part of the pacman package manager. // // Split packages and architecture dependent fields are fully supported. // // This Package aimes to parse srcinfos but not interpret them in any way. // All values are fundamentally strings, other tools should be used for // things such as dependency parsing, validity checking etc. package srcinfo import ( "fmt" ) // ArchString describes string values that may be architecture dependent. // For Example depends_x86_64. // If Arch is an empty string then the field is not architecture dependent. type ArchString struct { Arch string // Architecture name Value string // Value } // Package describes the fields of a pkgbuild that may be overwritten by // in build_ function. type Package struct { Pkgname string Pkgdesc string Arch []string URL string License []string Groups []string Depends []ArchString OptDepends []ArchString Provides []ArchString Conflicts []ArchString Replaces []ArchString Backup []string Options []string Install string Changelog string } // PackageBase describes the fields of a pkgbuild that may not be overwritten // in package_ function. type PackageBase struct { Pkgbase string Pkgver string Pkgrel string Epoch string Source []ArchString ValidPGPKeys []string NoExtract []string MD5Sums []ArchString SHA1Sums []ArchString SHA224Sums []ArchString SHA256Sums []ArchString SHA384Sums []ArchString SHA512Sums []ArchString MakeDepends []ArchString CheckDepends []ArchString } // Srcinfo represents a full srcinfo. All global fields are defined here while // fields overwritten in the package_ function are defined in the // Packages field. // // Note: The Packages field only contains the values that each package // overrides, global fields will be missing. A Package containing both global // and overwritten fields can be generated using the SplitPackage function. type Srcinfo struct { PackageBase // Fields that only apply to the package base Package // Fields that apply to the package globally Packages []Package // Fields for each package this package base contains } // EmptyOverride is used to signal when a value has been overridden with an // empty value. An empty ovrride is when a value is defined in the pkgbuild but // then overridden inside the package function to be empty. // // For example "pkgdesc=''" is an empty override on the pkgdesc which would // lead to the line "pkgdesc=" in the srcinfo. // // This value is used internally to store empty overrides, mainly to avoid // using string pointers. It is possible to check for empty overrides using // the Packages slice in Packagebase. // // During normal use with the SplitPackage function this value will be // converted back to an empty string, or removed entirely for slice values. // This means the this value can be completley ignored unless you are // explicitly looking for empty overrides. const EmptyOverride = "\x00" // Version formats a version string from the epoch, pkgver and pkgrel of the // srcinfo. In the format [epoch:]pkgver-pkgrel. func (si *Srcinfo) Version() string { if si.Epoch == "" { return si.Pkgver + "-" + si.Pkgrel } return si.Epoch + ":" + si.Pkgver + "-" + si.Pkgrel } // SplitPackages generates a splice of all packages that are part of this // srcinfo. This is equivalent to calling SplitPackage on every pkgname. func (si *Srcinfo) SplitPackages() []*Package { pkgs := make([]*Package, 0, len(si.Packages)) for _, pkg := range si.Packages { pkgs = append(pkgs, mergeSplitPackage(&si.Package, &pkg)) } return pkgs } // SplitPackage generates a Package that contains all fields that the specified // pkgname has. But will fall back on global fields if they are not defined in // the Package. // // Note slice values will be passed by reference, it is not recommended you // modify this struct after it is returned. func (si *Srcinfo) SplitPackage(pkgname string) (*Package, error) { for n := range si.Packages { if si.Packages[n].Pkgname == pkgname { return mergeSplitPackage(&si.Package, &si.Packages[n]), nil } } return nil, fmt.Errorf("Package \"%s\" is not part of the package base \"%s\"", pkgname, si.Pkgbase) } func mergeArchSlice(global, override []ArchString) []ArchString { overridden := make(map[string]struct{}) merged := make([]ArchString, 0, len(override)) for _, v := range override { overridden[v.Arch] = struct{}{} if v.Value == EmptyOverride { continue } merged = append(merged, v) } for _, v := range global { if _, ok := overridden[v.Arch]; !ok { merged = append(merged, v) } } return merged } func mergeSplitPackage(base, split *Package) *Package { pkg := &Package{} *pkg = *base pkg.Pkgname = split.Pkgname if split.Pkgdesc != "" { pkg.Pkgdesc = split.Pkgdesc } if len(split.Arch) != 0 { pkg.Arch = split.Arch } if split.URL != "" { pkg.URL = split.URL } if len(split.License) != 0 { pkg.License = split.License } if len(split.Groups) != 0 { pkg.Groups = split.Groups } if len(split.Depends) != 0 { pkg.Depends = mergeArchSlice(pkg.Depends, split.Depends) } if len(split.OptDepends) != 0 { pkg.OptDepends = mergeArchSlice(pkg.OptDepends, split.OptDepends) } if len(split.Provides) != 0 { pkg.Provides = mergeArchSlice(pkg.Provides, split.Provides) } if len(split.Conflicts) != 0 { pkg.Conflicts = mergeArchSlice(pkg.Conflicts, split.Conflicts) } if len(split.Replaces) != 0 { pkg.Replaces = mergeArchSlice(pkg.Replaces, split.Replaces) } if len(split.Backup) != 0 { pkg.Backup = split.Backup } if len(split.Options) != 0 { pkg.Options = split.Options } if split.Changelog != "" { pkg.Changelog = split.Changelog } if split.Install != "" { pkg.Install = split.Install } return pkg }