diff --git a/srcs/buildtool/args.go b/srcs/buildtool/args.go index 80e77df..8b3e13e 100644 --- a/srcs/buildtool/args.go +++ b/srcs/buildtool/args.go @@ -20,6 +20,7 @@ const ( objsArg = "objects" makefileArg = "makefile" configArg = "config" + patchArg = "patch" ) // ParseArguments parses arguments of the application. @@ -43,6 +44,8 @@ func parseLocalArguments(p *argparse.Parser, args *u.Arguments) error { "for Makefile"}) args.InitArgParse(p, args, u.STRINGLIST, "c", configArg, &argparse.Options{Required: false, Help: "Add configuration files"}) + args.InitArgParse(p, args, u.STRING, "", patchArg, + &argparse.Options{Required: false, Help: "Add patch files"}) return u.ParserWrapper(p, os.Args) } diff --git a/srcs/buildtool/run_buildtool.go b/srcs/buildtool/run_buildtool.go index 3fe54e3..d1d2d39 100644 --- a/srcs/buildtool/run_buildtool.go +++ b/srcs/buildtool/run_buildtool.go @@ -238,6 +238,19 @@ func RunBuildTool(homeDir string, data *u.Data) { panic(err) } + // Copy, conform and apply user-provided patches to the new unikernel folder + if len(*args.StringArg[patchArg]) > 0 { + // Create patch folder + patchFolder, err := createPatchFolder(appFolder) + if err != nil { + u.PrintErr(err) + } + if err := addAndApplyPatchFiles(*args.StringArg[patchArg], *patchFolder, appFolder); err != + nil { + u.PrintErr(err) + } + } + // Conform file include directives to the new unikernel folder organisation if err := conformIncludeDirectives(appFolder); err != nil { u.PrintErr(err) @@ -248,6 +261,7 @@ func RunBuildTool(homeDir string, data *u.Data) { // Match micro-libs matchedLibs, externalLibs, err := matchLibs(unikraftPath+"lib"+u.SEP, data) + if err != nil { u.PrintErr(err) } @@ -257,6 +271,7 @@ func RunBuildTool(homeDir string, data *u.Data) { // Match internal dependencies between micro-libs if err := searchInternalDependencies(workspacePath, &matchedLibs, + externalLibs); err != nil { u.PrintErr(err) } @@ -270,6 +285,7 @@ func RunBuildTool(homeDir string, data *u.Data) { // Generate Makefiles if err := generateMake(programName, appFolder, workspacePath, *args.StringArg[makefileArg], + matchedLibs, selectedFiles, externalLibs); err != nil { u.PrintErr(err) } @@ -282,7 +298,6 @@ func RunBuildTool(homeDir string, data *u.Data) { // Run make runMake(programName, appFolder) - } // retFolderCompat modifies its string argument in order to replace its underscore by a dash when diff --git a/srcs/buildtool/unikraft_files_process.go b/srcs/buildtool/unikraft_files_process.go index ccf028c..1bb31de 100644 --- a/srcs/buildtool/unikraft_files_process.go +++ b/srcs/buildtool/unikraft_files_process.go @@ -18,6 +18,18 @@ import ( u "tools/srcs/common" ) +// ---------------------------Create Patches Folder----------------------------- + +func createPatchFolder(appFolder string) (*string, error) { + + patchesFolder := appFolder + "patches" + u.SEP + if _, err := u.CreateFolder(patchesFolder); err != nil { + return nil, err + } + + return &patchesFolder, nil +} + // ---------------------------Create Include Folder----------------------------- func createIncludeFolder(appFolder string) (*string, error) { @@ -154,11 +166,11 @@ var srcLanguages = map[string]int{ ".c": 0, ".cpp": 0, ".cc": 0, - //".S": 0, - //".s": 0, - //".asm": 0, - //".py": 0, - //".go": 0, + ".S": 0, + ".s": 0, + ".asm": 0, + ".py": 0, + ".go": 0, } func filterSourcesFiles(sourceFiles []string) []string { @@ -174,14 +186,134 @@ func filterSourcesFiles(sourceFiles []string) []string { return filterSrcFiles } +// addAndApplyPatchFiles copies all the user-provided patch files to the unikernel directory, +// conforms them to the unikernel directory format so that all paths in the patch files are paths +// to source files located in the unikernel folder and applies the patches. +// +// It returns an error if any, otherwise it returns nil. +func addAndApplyPatchFiles(patchPath string, patchFolder, appFolder string) error { + + // Copy and conform patch files + err := filepath.Walk(patchPath, func(filePath string, info os.FileInfo, + err error) error { + + if !info.IsDir() { + extension := filepath.Ext(info.Name()) + if extension == ".patch" { + if err = conformPatchFile(filePath, patchFolder+info.Name()); err != nil { + return err + } + } + } + return nil + }) + if err != nil { + return err + } + + // Initialise git repo to be able to apply patches + _, _, _ = u.ExecuteRunCmd("git", appFolder, true, "init") + _, _, _ = u.ExecuteRunCmd("git", appFolder, true, "add", ".") + _, _, _ = u.ExecuteRunCmd("git", appFolder, true, "commit", "-m", "first commit") + + // Apply patches + err = filepath.Walk(patchPath, func(filePath string, info os.FileInfo, + err error) error { + + if !info.IsDir() { + _, _, _ = u.ExecuteRunCmd("git", appFolder, true, "am", patchFolder+info.Name()) + } + return nil + }) + if err != nil { + return err + } + + return nil +} + +// conformPatchPath conforms a path in a user-provided patch file to the unikernel directory format +// so that this path describes a source file located in the unikernel folder. +func conformPatchPath(match *[]string, index int) { + extension := filepath.Ext((*match)[index]) + if extension == ".h" || extension == ".hpp" || extension == ".hcc" { + (*match)[index] = "include" + u.SEP + filepath.Base((*match)[index]) + } else if extension == ".c" || extension == ".cpp" || extension == ".cc" { + (*match)[index] = filepath.Base((*match)[index]) + } else { + u.PrintWarning("Unsupported extension for file: " + + filepath.Base((*match)[index])) + } +} + +// conformPatchFile copies all the user-provided patch files to the unikernel directory and +// conforms them to the unikernel directory format so that all paths in the patch files are paths +// to source files located in the unikernel folder. +// +// It returns an error if any, otherwise it returns nil. +func conformPatchFile(patchPath, newPatchPath string) error { + + patchLines, err := u.ReadLinesFile(patchPath) + if err != nil { + return err + } + + // Find paths in patch file using regexp + var re1 = regexp.MustCompile(`( )(.*)( \| )(.*)`) + var re2 = regexp.MustCompile(`(diff --git )(a/)?(.*)( )(b/)?(.*)`) + var re3 = regexp.MustCompile(`(--- )(a/)?(.*)`) + var re4 = regexp.MustCompile(`(\+\+\+ )(b/)?(.*)`) + + for lineIndex := range patchLines { + + // All paths to files to be modified by the patch are listed under "---" + if patchLines[lineIndex] == "---\n" { + lineIndex++ + for ; !strings.Contains(patchLines[lineIndex], "changed"); lineIndex++ { + for _, match := range re1.FindAllStringSubmatch(patchLines[lineIndex], -1) { + conformPatchPath(&match, 2) + patchLines[lineIndex] = strings.Join(match[1:], "") + "\n" + } + } + } + + // All diff lines contain paths to files to be modified by the patch + if len(patchLines[lineIndex]) > 10 && patchLines[lineIndex][:10] == "diff --git" { + for _, match := range re2.FindAllStringSubmatch(patchLines[lineIndex], -1) { + conformPatchPath(&match, 3) + conformPatchPath(&match, 6) + patchLines[lineIndex] = strings.Join(match[1:], "") + "\n" + } + + // Same observation for the two lines following the index line + for _, match := range re3.FindAllStringSubmatch(patchLines[lineIndex+2], -1) { + conformPatchPath(&match, 3) + patchLines[lineIndex+2] = strings.Join(match[1:], "") + "\n" + } + for _, match := range re4.FindAllStringSubmatch(patchLines[lineIndex+3], -1) { + conformPatchPath(&match, 3) + patchLines[lineIndex+3] = strings.Join(match[1:], "") + "\n" + } + } + } + + // Write the modified content to a file in the unikernel folder + err = u.WriteToFile(newPatchPath, []byte(strings.Join(patchLines, ""))) + if err != nil { + return err + } + + return nil +} + // conformIncludeDirectives conforms all the user-defined include directives of all C/C++ source // files to the unikernel directory format so that all these directives are paths to header files // located in the include folder of the unikernel directory. // // It returns an error if any, otherwise it returns nil. -func conformIncludeDirectives(sourcesPath string) error { +func conformIncludeDirectives(sourcePath string) error { - err := filepath.Walk(sourcesPath, func(path string, info os.FileInfo, + err := filepath.Walk(sourcePath, func(path string, info os.FileInfo, err error) error { if !info.IsDir() { @@ -204,6 +336,7 @@ func conformIncludeDirectives(sourcesPath string) error { if err != nil { return err } + return nil } @@ -212,9 +345,9 @@ func conformIncludeDirectives(sourcesPath string) error { // include folder. // // It returns an error if any, otherwise it returns nil. -func conformFile(path string, isHeader bool) (err error) { +func conformFile(filePath string, isHeader bool) (err error) { - fileLines, err := u.ReadLinesFile(path) + fileLines, err := u.ReadLinesFile(filePath) if err != nil { return err } @@ -222,10 +355,10 @@ func conformFile(path string, isHeader bool) (err error) { // Find user-defined include directives using regexp var re = regexp.MustCompile(`(.*)(#include)(.*)(")(.*)(")(.*)`) - for index := range fileLines { - for _, match := range re.FindAllStringSubmatch(fileLines[index], -1) { + for lineIndex := range fileLines { + for _, match := range re.FindAllStringSubmatch(fileLines[lineIndex], -1) { - // Replace the path by its last element + // Replace the path by (include/ +) its last element for i := 1; i < len(match); i++ { if match[i] == "\"" { if isHeader { @@ -233,7 +366,7 @@ func conformFile(path string, isHeader bool) (err error) { } else { match[i+1] = "include" + u.SEP + filepath.Base(match[i+1]) } - fileLines[index] = strings.Join(match[1:], "") + "\n" + fileLines[lineIndex] = strings.Join(match[1:], "") + "\n" break } } @@ -241,7 +374,7 @@ func conformFile(path string, isHeader bool) (err error) { } // Write the modified content to a file in the unikernel folder - err = u.WriteToFile(path, []byte(strings.Join(fileLines, ""))) + err = u.WriteToFile(filePath, []byte(strings.Join(fileLines, ""))) if err != nil { return err } diff --git a/srcs/common/git.go b/srcs/common/git.go index 5fa8dda..0ff16f5 100644 --- a/srcs/common/git.go +++ b/srcs/common/git.go @@ -6,7 +6,7 @@ package common -const branch = "RELEASE-0.10.0" +const branch = "RELEASE-0.9.0" // GitCloneRepository clones a git repository at the the given url. //