This directory doesn't exist all the time, e.g. if the kernel was built without modules or no modules were installed for some reason. Assume the kernel package knows what it is doing and just print a message that might be helpful if the kernel package ends up not knowing what it is doing.
711 lines
17 KiB
Go
711 lines
17 KiB
Go
// Copyright 2021 Clayton Craft <clayton@craftyguy.net>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"debug/elf"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.sr.ht/~sircmpwn/getopt"
|
|
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/archive"
|
|
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/deviceinfo"
|
|
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/misc"
|
|
)
|
|
|
|
func timeFunc(start time.Time, name string) {
|
|
elapsed := time.Since(start)
|
|
log.Printf("%s completed in: %s", name, elapsed)
|
|
}
|
|
|
|
func main() {
|
|
devinfo, err := deviceinfo.ReadDeviceinfo()
|
|
if err != nil {
|
|
log.Print("NOTE: deviceinfo (from device package) not installed yet, " +
|
|
"not building the initramfs now (it should get built later " +
|
|
"automatically.)")
|
|
os.Exit(0)
|
|
}
|
|
|
|
var outDir string
|
|
getopt.StringVar(&outDir, "d", "/boot", "Directory to output initfs(-extra) and other boot files, default: /boot")
|
|
|
|
if err := getopt.Parse(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
defer timeFunc(time.Now(), "mkinitfs")
|
|
|
|
kernVer, err := getKernelVersion()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// temporary working dir
|
|
workDir, err := ioutil.TempDir("", "mkinitfs")
|
|
if err != nil {
|
|
log.Fatal("Unable to create temporary work directory:", err)
|
|
}
|
|
defer os.RemoveAll(workDir)
|
|
|
|
log.Print("Generating for kernel version: ", kernVer)
|
|
log.Print("Output directory: ", outDir)
|
|
|
|
if err := generateInitfs("initramfs", workDir, kernVer, devinfo); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if err := generateInitfsExtra("initramfs-extra", workDir, devinfo); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Final processing of initramfs / kernel is done by boot-deploy
|
|
if err := bootDeploy(workDir, outDir); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
}
|
|
|
|
func bootDeploy(workDir string, outDir string) error {
|
|
// boot-deploy expects the kernel to be in the same dir as initramfs.
|
|
// Assume that the kernel is in the output dir...
|
|
log.Print("== Using boot-deploy to finalize/install files ==")
|
|
kernels, _ := filepath.Glob(filepath.Join(outDir, "vmlinuz*"))
|
|
if len(kernels) == 0 {
|
|
return errors.New("Unable to find any kernels at " + filepath.Join(outDir, "vmlinuz*"))
|
|
}
|
|
kernFile, err := os.Open(kernels[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer kernFile.Close()
|
|
|
|
kernFileCopy, err := os.Create(filepath.Join(workDir, "vmlinuz"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err = io.Copy(kernFileCopy, kernFile); err != nil {
|
|
return err
|
|
}
|
|
kernFileCopy.Close()
|
|
|
|
// boot-deploy -i initramfs -k vmlinuz-postmarketos-rockchip -d /tmp/cpio -o /tmp/foo initramfs-extra
|
|
cmd := exec.Command("boot-deploy",
|
|
"-i", "initramfs",
|
|
"-k", "vmlinuz",
|
|
"-d", workDir,
|
|
"-o", outDir,
|
|
"initramfs-extra")
|
|
if !exists(cmd.Path) {
|
|
return errors.New("boot-deploy command not found.")
|
|
}
|
|
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
// err is ignored, since shellcheck will return != 0 if there are issues
|
|
if err := cmd.Run(); err != nil {
|
|
log.Print("'boot-deploy' command failed: ")
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createInitfsRootDirs(initfsRoot string) {
|
|
dirs := []string{
|
|
"/bin", "/sbin", "/usr/bin", "/usr/lib", "/usr/sbin", "/proc", "/sys",
|
|
"/dev", "/tmp", "/lib", "/boot", "/sysroot", "/etc",
|
|
}
|
|
|
|
for _, dir := range dirs {
|
|
if err := os.MkdirAll(filepath.Join(initfsRoot, dir), os.FileMode(0775)); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func exists(file string) bool {
|
|
if _, err := os.Stat(file); err == nil {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func getHookFiles(filesdir string) misc.StringSet {
|
|
fileInfo, err := ioutil.ReadDir(filesdir)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
files := make(misc.StringSet)
|
|
for _, file := range fileInfo {
|
|
path := filepath.Join(filesdir, file.Name())
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
s := bufio.NewScanner(f)
|
|
for s.Scan() {
|
|
if !exists(s.Text()) {
|
|
log.Fatalf("Unable to find file %q required by %q", s.Text(), path)
|
|
}
|
|
files[s.Text()] = false
|
|
}
|
|
if err := s.Err(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
return files
|
|
}
|
|
|
|
// Recursively list all dependencies for a given ELF binary
|
|
func getBinaryDeps(files misc.StringSet, file string) error {
|
|
// if file is a symlink, resolve dependencies for target
|
|
fileStat, err := os.Lstat(file)
|
|
if err != nil {
|
|
log.Print("getBinaryDeps: failed to stat file")
|
|
return err
|
|
}
|
|
|
|
// Symlink: write symlink to archive then set 'file' to link target
|
|
if fileStat.Mode()&os.ModeSymlink != 0 {
|
|
target, err := os.Readlink(file)
|
|
if err != nil {
|
|
log.Print("getBinaryDeps: unable to read symlink: ", file)
|
|
return err
|
|
}
|
|
if !filepath.IsAbs(target) {
|
|
target, err = misc.RelativeSymlinkTargetToDir(target, filepath.Dir(file))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := getBinaryDeps(files, target); err != nil {
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
|
|
// get dependencies for binaries
|
|
fd, err := elf.Open(file)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
libs, _ := fd.ImportedLibraries()
|
|
fd.Close()
|
|
files[file] = false
|
|
|
|
if len(libs) == 0 {
|
|
return err
|
|
}
|
|
|
|
libdirs := []string{"/usr/lib", "/lib"}
|
|
for _, lib := range libs {
|
|
found := false
|
|
for _, libdir := range libdirs {
|
|
path := filepath.Join(libdir, lib)
|
|
if _, err := os.Stat(path); err == nil {
|
|
err := getBinaryDeps(files, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
files[path] = false
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
log.Fatalf("Unable to locate dependency for %q: %s", file, lib)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getFiles(files misc.StringSet, newFiles misc.StringSet, required bool) error {
|
|
for file := range newFiles {
|
|
err := getFile(files, file, required)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getFile(files misc.StringSet, file string, required bool) error {
|
|
if !exists(file) {
|
|
if required {
|
|
return errors.New("getFile: File does not exist :" + file)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
files[file] = false
|
|
|
|
// get dependencies for binaries
|
|
if _, err := elf.Open(file); err != nil {
|
|
// file is not an elf, so don't resolve lib dependencies
|
|
return nil
|
|
}
|
|
|
|
err := getBinaryDeps(files, file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getOskConfFontPath(oskConfPath string) (string, error) {
|
|
var path string
|
|
f, err := os.Open(oskConfPath)
|
|
if err != nil {
|
|
return path, err
|
|
}
|
|
defer f.Close()
|
|
s := bufio.NewScanner(f)
|
|
for s.Scan() {
|
|
fields := strings.Fields(s.Text())
|
|
// "key = val" is 3 fields
|
|
if len(fields) > 2 && fields[0] == "keyboard-font" {
|
|
path = fields[2]
|
|
}
|
|
}
|
|
if !exists(path) {
|
|
return path, errors.New("Unable to find font: " + path)
|
|
}
|
|
|
|
return path, nil
|
|
}
|
|
|
|
// Get a list of files and their dependencies related to supporting rootfs full
|
|
// disk (d)encryption
|
|
func getFdeFiles(files misc.StringSet, devinfo deviceinfo.DeviceInfo) error {
|
|
confFiles := misc.StringSet{
|
|
"/etc/osk.conf": false,
|
|
"/etc/ts.conf": false,
|
|
"/etc/pointercal": false,
|
|
"/etc/fb.modes": false,
|
|
"/etc/directfbrc": false,
|
|
}
|
|
// TODO: this shouldn't be false? though some files (pointercal) don't always exist...
|
|
if err := getFiles(files, confFiles, false); err != nil {
|
|
return err
|
|
}
|
|
|
|
// osk-sdl
|
|
oskFiles := misc.StringSet{
|
|
"/usr/bin/osk-sdl": false,
|
|
"/sbin/cryptsetup": false,
|
|
"/usr/lib/libGL.so.1": false}
|
|
if err := getFiles(files, oskFiles, true); err != nil {
|
|
return err
|
|
}
|
|
|
|
fontFile, err := getOskConfFontPath("/etc/osk.conf")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
files[fontFile] = false
|
|
|
|
// Directfb
|
|
dfbFiles := make(misc.StringSet)
|
|
err = filepath.Walk("/usr/lib/directfb-1.7-7", func(path string, f os.FileInfo, err error) error {
|
|
if filepath.Ext(path) == ".so" {
|
|
dfbFiles[path] = false
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
log.Print("getBinaryDeps: failed to stat file")
|
|
return err
|
|
}
|
|
if err := getFiles(files, dfbFiles, true); err != nil {
|
|
return err
|
|
}
|
|
|
|
// tslib
|
|
tslibFiles := make(misc.StringSet)
|
|
err = filepath.Walk("/usr/lib/ts", func(path string, f os.FileInfo, err error) error {
|
|
if filepath.Ext(path) == ".so" {
|
|
tslibFiles[path] = false
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
log.Print("getBinaryDeps: failed to stat file")
|
|
return err
|
|
}
|
|
libts, _ := filepath.Glob("/usr/lib/libts*")
|
|
for _, file := range libts {
|
|
tslibFiles[file] = false
|
|
}
|
|
if err = getFiles(files, tslibFiles, true); err != nil {
|
|
return err
|
|
}
|
|
|
|
// mesa hw accel
|
|
if devinfo.Deviceinfo_mesa_driver != "" {
|
|
mesaFiles := misc.StringSet{
|
|
"/usr/lib/libEGL.so.1": false,
|
|
"/usr/lib/libGLESv2.so.2": false,
|
|
"/usr/lib/libgbm.so.1": false,
|
|
"/usr/lib/libudev.so.1": false,
|
|
"/usr/lib/xorg/modules/dri/" + devinfo.Deviceinfo_mesa_driver + "_dri.so": false,
|
|
}
|
|
if err := getFiles(files, mesaFiles, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getHookScripts(files misc.StringSet) {
|
|
scripts, _ := filepath.Glob("/etc/postmarketos-mkinitfs/hooks/*.sh")
|
|
for _, script := range scripts {
|
|
files[script] = false
|
|
}
|
|
}
|
|
|
|
func getInitfsExtraFiles(files misc.StringSet, devinfo deviceinfo.DeviceInfo) error {
|
|
log.Println("== Generating initramfs extra ==")
|
|
binariesExtra := misc.StringSet{
|
|
"/lib/libz.so.1": false,
|
|
"/sbin/dmsetup": false,
|
|
"/sbin/e2fsck": false,
|
|
"/usr/sbin/parted": false,
|
|
"/usr/sbin/resize2fs": false,
|
|
"/usr/sbin/resize.f2fs": false,
|
|
}
|
|
log.Println("- Including extra binaries")
|
|
if err := getFiles(files, binariesExtra, true); err != nil {
|
|
return err
|
|
}
|
|
|
|
if exists("/usr/bin/osk-sdl") {
|
|
log.Println("- Including FDE support")
|
|
if err := getFdeFiles(files, devinfo); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
log.Println("- *NOT* including FDE support")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getInitfsFiles(files misc.StringSet, devinfo deviceinfo.DeviceInfo) error {
|
|
log.Println("== Generating initramfs ==")
|
|
requiredFiles := misc.StringSet{
|
|
"/bin/busybox": false,
|
|
"/bin/sh": false,
|
|
"/bin/busybox-extras": false,
|
|
"/usr/sbin/telnetd": false,
|
|
"/sbin/kpartx": false,
|
|
"/etc/deviceinfo": false,
|
|
}
|
|
|
|
// Hook files & scripts
|
|
if exists("/etc/postmarketos-mkinitfs/files") {
|
|
log.Println("- Including hook files")
|
|
hookFiles := getHookFiles("/etc/postmarketos-mkinitfs/files")
|
|
if err := getFiles(files, hookFiles, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
log.Println("- Including hook scripts")
|
|
getHookScripts(files)
|
|
|
|
log.Println("- Including required binaries")
|
|
if err := getFiles(files, requiredFiles, true); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getInitfsModules(files misc.StringSet, devinfo deviceinfo.DeviceInfo, kernelVer string) error {
|
|
log.Println("- Including kernel modules")
|
|
|
|
modDir := filepath.Join("/lib/modules", kernelVer)
|
|
if !exists(modDir) {
|
|
// dir /lib/modules/<kernel> if kernel built without module support, so just print a message
|
|
log.Printf("-- kernel module directory not found: %q, not including modules", modDir)
|
|
return nil
|
|
}
|
|
|
|
// modules.* required by modprobe
|
|
modprobeFiles, _ := filepath.Glob(filepath.Join(modDir, "modules.*"))
|
|
for _, file := range modprobeFiles {
|
|
files[file] = false
|
|
}
|
|
|
|
// module name (without extension), or directory (trailing slash is important! globs OK)
|
|
requiredModules := []string{
|
|
"loop",
|
|
"dm-crypt",
|
|
"kernel/fs/overlayfs/",
|
|
"kernel/crypto/",
|
|
"kernel/arch/*/crypto/",
|
|
}
|
|
|
|
for _, item := range requiredModules {
|
|
dir, file := filepath.Split(item)
|
|
if file == "" {
|
|
// item is a directory
|
|
dir = filepath.Join(modDir, dir)
|
|
dirs, _ := filepath.Glob(dir)
|
|
for _, d := range dirs {
|
|
if err := getModulesInDir(files, d); err != nil {
|
|
log.Print("Unable to get modules in dir: ", d)
|
|
return err
|
|
}
|
|
}
|
|
continue
|
|
} else if dir == "" {
|
|
// item is a module name
|
|
if err := getModule(files, file, modDir); err != nil {
|
|
log.Print("Unable to get module: ", file)
|
|
return err
|
|
}
|
|
continue
|
|
} else {
|
|
log.Printf("Unknown module entry: %q", item)
|
|
}
|
|
}
|
|
|
|
// deviceinfo modules
|
|
for _, module := range strings.Fields(devinfo.Deviceinfo_modules_initfs) {
|
|
if err := getModule(files, module, modDir); err != nil {
|
|
log.Print("Unable to get modules from deviceinfo")
|
|
return err
|
|
}
|
|
}
|
|
|
|
// /etc/postmarketos-mkinitfs/modules/*.modules
|
|
initfsModFiles, _ := filepath.Glob("/etc/postmarketos-mkinitfs/modules/*.modules")
|
|
for _, modFile := range initfsModFiles {
|
|
f, err := os.Open(modFile)
|
|
if err != nil {
|
|
log.Print("getInitfsModules: unable to open mkinitfs modules file: ", modFile)
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
s := bufio.NewScanner(f)
|
|
for s.Scan() {
|
|
if err := getModule(files, s.Text(), modDir); err != nil {
|
|
log.Print("getInitfsModules: unable to get module file: ", s.Text())
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getKernelReleaseFile() (string, error) {
|
|
files, _ := filepath.Glob("/usr/share/kernel/*/kernel.release")
|
|
// only one kernel flavor supported
|
|
if len(files) != 1 {
|
|
return "", errors.New(fmt.Sprintf("Only one kernel release/flavor is supported, found: %q", files))
|
|
}
|
|
|
|
return files[0], nil
|
|
}
|
|
|
|
func getKernelVersion() (string, error) {
|
|
var version string
|
|
|
|
releaseFile, err := getKernelReleaseFile()
|
|
if err != nil {
|
|
return version, err
|
|
}
|
|
|
|
contents, err := os.ReadFile(releaseFile)
|
|
if err != nil {
|
|
return version, err
|
|
}
|
|
|
|
return strings.TrimSpace(string(contents)), nil
|
|
}
|
|
|
|
func generateInitfs(name string, path string, kernVer string, devinfo deviceinfo.DeviceInfo) error {
|
|
initfsArchive, err := archive.New()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
requiredDirs := []string{
|
|
"/bin", "/sbin", "/usr/bin", "/usr/sbin", "/proc", "/sys",
|
|
"/dev", "/tmp", "/lib", "/boot", "/sysroot", "/etc",
|
|
}
|
|
for _, dir := range requiredDirs {
|
|
initfsArchive.Dirs[dir] = false
|
|
}
|
|
|
|
if err := getInitfsFiles(initfsArchive.Files, devinfo); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := getInitfsModules(initfsArchive.Files, devinfo, kernVer); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := initfsArchive.AddFile("/usr/share/postmarketos-mkinitfs/init.sh", "/init"); err != nil {
|
|
return err
|
|
}
|
|
|
|
// splash images
|
|
log.Println("- Including splash images")
|
|
splashFiles, _ := filepath.Glob("/usr/share/postmarketos-splashes/*.ppm.gz")
|
|
for _, file := range splashFiles {
|
|
// splash images are expected at /<file>
|
|
if err := initfsArchive.AddFile(file, filepath.Join("/", filepath.Base(file))); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// initfs_functions
|
|
if err := initfsArchive.AddFile("/usr/share/postmarketos-mkinitfs/init_functions.sh", "/init_functions.sh"); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Println("- Writing and verifying initramfs archive")
|
|
if err := initfsArchive.Write(filepath.Join(path, name), os.FileMode(0644)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func generateInitfsExtra(name string, path string, devinfo deviceinfo.DeviceInfo) error {
|
|
initfsExtraArchive, err := archive.New()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := getInitfsExtraFiles(initfsExtraArchive.Files, devinfo); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Println("- Writing and verifying initramfs-extra archive")
|
|
if err := initfsExtraArchive.Write(filepath.Join(path, name), os.FileMode(0644)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func stripExts(file string) string {
|
|
for {
|
|
if filepath.Ext(file) == "" {
|
|
break
|
|
}
|
|
file = strings.TrimSuffix(file, filepath.Ext(file))
|
|
}
|
|
return file
|
|
}
|
|
|
|
func getModulesInDir(files misc.StringSet, modPath string) error {
|
|
err := filepath.Walk(modPath, func(path string, f os.FileInfo, err error) error {
|
|
// TODO: need to support more extensions?
|
|
if filepath.Ext(path) != ".ko" && filepath.Ext(path) != ".xz" {
|
|
return nil
|
|
}
|
|
files[path] = false
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Given a module name, e.g. 'dwc_wdt', resolve the full path to the module
|
|
// file and all of its dependencies.
|
|
// Note: it's not necessarily fatal if the module is not found, since it may
|
|
// have been built into the kernel
|
|
// TODO: look for it in modules.builtin, and make it fatal if it can't be found
|
|
// anywhere
|
|
func getModule(files misc.StringSet, modName string, modDir string) error {
|
|
|
|
deps, err := getModuleDeps(modName, modDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(deps) == 0 {
|
|
// retry and swap - and _ in module name
|
|
if strings.Contains(modName, "-") {
|
|
modName = strings.ReplaceAll(modName, "-", "_")
|
|
} else {
|
|
modName = strings.ReplaceAll(modName, "_", "-")
|
|
}
|
|
deps, err = getModuleDeps(modName, modDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for _, dep := range deps {
|
|
p := filepath.Join(modDir, dep)
|
|
if !exists(p) {
|
|
log.Print(fmt.Sprintf("Tried to include a module that doesn't exist in the modules directory (%s): %s", modDir, p))
|
|
return err
|
|
}
|
|
files[p] = false
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func getModuleDeps(modName string, modDir string) ([]string, error) {
|
|
var deps []string
|
|
|
|
modDep := filepath.Join(modDir, "modules.dep")
|
|
if !exists(modDep) {
|
|
log.Fatal("Kernel module.dep not found: ", modDir)
|
|
}
|
|
|
|
fd, err := os.Open(modDep)
|
|
if err != nil {
|
|
log.Print("Unable to open modules.dep: ", modDep)
|
|
return deps, err
|
|
}
|
|
|
|
defer fd.Close()
|
|
s := bufio.NewScanner(fd)
|
|
for s.Scan() {
|
|
fields := strings.Fields(s.Text())
|
|
fields[0] = strings.TrimSuffix(fields[0], ":")
|
|
if modName != filepath.Base(stripExts(fields[0])) {
|
|
continue
|
|
}
|
|
for _, modPath := range fields {
|
|
deps = append(deps, modPath)
|
|
}
|
|
}
|
|
if err := s.Err(); err != nil {
|
|
log.Print("Unable to get module + dependencies: ", modName)
|
|
return deps, err
|
|
}
|
|
|
|
return deps, nil
|
|
}
|