added interdependence graph generation argument
This commit is contained in:
parent
6b5ed45943
commit
b5ec3c3329
4 changed files with 229 additions and 44 deletions
|
@ -7,7 +7,6 @@
|
||||||
package buildtool
|
package buildtool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -106,6 +105,32 @@ func parseMakeOutput(output string) string {
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addConfigFiles adds user-provided configuration files to the app unikernel folder.
|
||||||
|
func addConfigFiles(configFiles []string, selectedFiles *[]string, includeFolder,
|
||||||
|
appFolder string) {
|
||||||
|
|
||||||
|
for _, configFilePath := range configFiles {
|
||||||
|
configFile := filepath.Base(configFilePath)
|
||||||
|
fileExt := filepath.Ext(configFile)
|
||||||
|
|
||||||
|
// Copy config file
|
||||||
|
if fileExt == ".h" || fileExt == ".hpp" || fileExt == ".hcc" {
|
||||||
|
if err := u.CopyFileContents(configFilePath, includeFolder+configFile); err != nil {
|
||||||
|
u.PrintErr(err)
|
||||||
|
}
|
||||||
|
} else if fileExt == ".c" || fileExt == ".cpp" || fileExt == ".cc" {
|
||||||
|
if err := u.CopyFileContents(configFilePath, appFolder+configFile); err != nil {
|
||||||
|
u.PrintErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Makefile.uk entry
|
||||||
|
*selectedFiles = append(*selectedFiles, configFile)
|
||||||
|
} else {
|
||||||
|
u.PrintWarning("Unsupported extension for file: " + configFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------Run-------------------------------------
|
// -------------------------------------Run-------------------------------------
|
||||||
|
|
||||||
// RunBuildTool runs the automatic build tool to build a unikernel of a
|
// RunBuildTool runs the automatic build tool to build a unikernel of a
|
||||||
|
@ -123,8 +148,6 @@ func RunBuildTool(homeDir string, data *u.Data) {
|
||||||
u.PrintErr(err)
|
u.PrintErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(args)
|
|
||||||
fmt.Println(*args.StringListArg[configArg])
|
|
||||||
// Get program Name
|
// Get program Name
|
||||||
programName := *args.StringArg[programArg]
|
programName := *args.StringArg[programArg]
|
||||||
|
|
||||||
|
@ -216,25 +239,7 @@ func RunBuildTool(homeDir string, data *u.Data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move config files to Unikraft folder
|
// Move config files to Unikraft folder
|
||||||
configArg := *args.StringListArg[configArg]
|
addConfigFiles(*args.StringListArg[configArg], &selectedFiles, *includeFolder, appFolder)
|
||||||
for _, configFilePath := range configArg {
|
|
||||||
pathSplit := strings.Split(configFilePath, "/")
|
|
||||||
file := pathSplit[len(pathSplit)-1]
|
|
||||||
fileSplit := strings.Split(file, ".")
|
|
||||||
ext := fileSplit[len(fileSplit)-1]
|
|
||||||
|
|
||||||
if ext == "h" || ext == "hpp" || ext == "hcc" {
|
|
||||||
if err = u.CopyFileContents(configFilePath, *includeFolder+file); err != nil {
|
|
||||||
u.PrintErr(err)
|
|
||||||
}
|
|
||||||
} else if ext == "c" || ext == "cpp" || ext == "cc" {
|
|
||||||
if err = u.CopyFileContents(configFilePath, appFolder+file); err != nil {
|
|
||||||
u.PrintErr(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
u.PrintWarning("Unsupported extension for file: " + file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match micro-libs
|
// Match micro-libs
|
||||||
matchedLibs, externalLibs, err := matchLibs(unikraftPath+"lib"+u.SEP, data)
|
matchedLibs, externalLibs, err := matchLibs(unikraftPath+"lib"+u.SEP, data)
|
||||||
|
@ -242,8 +247,6 @@ func RunBuildTool(homeDir string, data *u.Data) {
|
||||||
u.PrintErr(err)
|
u.PrintErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("\nPREFINAL\n")
|
|
||||||
fmt.Println(matchedLibs)
|
|
||||||
// Clone the external git repositories
|
// Clone the external git repositories
|
||||||
cloneLibsFolders(workspacePath, matchedLibs, externalLibs)
|
cloneLibsFolders(workspacePath, matchedLibs, externalLibs)
|
||||||
|
|
||||||
|
@ -257,8 +260,6 @@ func RunBuildTool(homeDir string, data *u.Data) {
|
||||||
u.PrintOk("Match lib: " + lib)
|
u.PrintOk("Match lib: " + lib)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("\nFINAL\n")
|
|
||||||
fmt.Println(matchedLibs)
|
|
||||||
// Clone the external git repositories (if changed)
|
// Clone the external git repositories (if changed)
|
||||||
cloneLibsFolders(workspacePath, matchedLibs, externalLibs)
|
cloneLibsFolders(workspacePath, matchedLibs, externalLibs)
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
package dependtool
|
package dependtool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/akamensky/argparse"
|
|
||||||
"os"
|
"os"
|
||||||
u "tools/srcs/common"
|
u "tools/srcs/common"
|
||||||
|
|
||||||
|
"github.com/akamensky/argparse"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -22,6 +23,7 @@ const (
|
||||||
fullDepsArg = "fullDeps"
|
fullDepsArg = "fullDeps"
|
||||||
fullStaticAnalysis = "fullStaticAnalysis"
|
fullStaticAnalysis = "fullStaticAnalysis"
|
||||||
typeAnalysis = "typeAnalysis"
|
typeAnalysis = "typeAnalysis"
|
||||||
|
interdependArg = "interdepend"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseLocalArguments parses arguments of the application.
|
// parseLocalArguments parses arguments of the application.
|
||||||
|
@ -53,6 +55,9 @@ func parseLocalArguments(p *argparse.Parser, args *u.Arguments) error {
|
||||||
args.InitArgParse(p, args, u.INT, "", typeAnalysis,
|
args.InitArgParse(p, args, u.INT, "", typeAnalysis,
|
||||||
&argparse.Options{Required: false, Default: 0,
|
&argparse.Options{Required: false, Default: 0,
|
||||||
Help: "Kind of analysis (0: both; 1: static; 2: dynamic)"})
|
Help: "Kind of analysis (0: both; 1: static; 2: dynamic)"})
|
||||||
|
args.InitArgParse(p, args, u.BOOL, "i", interdependArg,
|
||||||
|
&argparse.Options{Required: false, Default: false,
|
||||||
|
Help: "Create the source files interdependence graph"})
|
||||||
|
|
||||||
return u.ParserWrapper(p, os.Args)
|
return u.ParserWrapper(p, os.Args)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
u "tools/srcs/common"
|
u "tools/srcs/common"
|
||||||
|
@ -11,6 +13,110 @@ import (
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// sourceFileIncludesAnalysis collects all the include directives from a C/C++ source file.
|
||||||
|
//
|
||||||
|
// It returns a slice containing the found header names.
|
||||||
|
func sourceFileIncludesAnalysis(sourceFile string) []string {
|
||||||
|
|
||||||
|
var fileIncludes []string
|
||||||
|
|
||||||
|
fileLines, err := u.ReadLinesFile(sourceFile)
|
||||||
|
if err != nil {
|
||||||
|
u.PrintErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range fileLines {
|
||||||
|
if strings.Contains(line, "#include") {
|
||||||
|
line = strings.ReplaceAll(line, " ", "")
|
||||||
|
line := strings.Split(line, "#include")[1]
|
||||||
|
if strings.HasPrefix(line, "\"") {
|
||||||
|
fileIncludes = append(fileIncludes, line[1:strings.Index(line[1:], "\"")+1])
|
||||||
|
} else if strings.HasPrefix(line, "<") {
|
||||||
|
fileIncludes = append(fileIncludes, line[1:strings.Index(line[1:], ">")+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileIncludes
|
||||||
|
}
|
||||||
|
|
||||||
|
// gccSourceFileIncludesAnalysis collects all the include directives from a C/C++ source file using
|
||||||
|
// the gcc preprocessor.
|
||||||
|
//
|
||||||
|
// It returns a slice containing the found header names.
|
||||||
|
func gccSourceFileIncludesAnalysis(sourceFile, tmpFolder string) []string {
|
||||||
|
|
||||||
|
var fileIncludes []string
|
||||||
|
|
||||||
|
outputStr, _ := ExecuteCommand("gcc", []string{"-E", sourceFile, "-I", tmpFolder})
|
||||||
|
outputSlice := strings.Split(outputStr, "\n")
|
||||||
|
|
||||||
|
for _, line := range outputSlice {
|
||||||
|
|
||||||
|
// Only interested in headers not coming from the standard library
|
||||||
|
if strings.Contains(line, "\""+tmpFolder) {
|
||||||
|
line = strings.Split(line, "\""+tmpFolder)[1]
|
||||||
|
includeDirective := line[0:strings.Index(line[0:], "\"")]
|
||||||
|
if !u.Contains(fileIncludes, includeDirective) {
|
||||||
|
fileIncludes = append(fileIncludes, includeDirective)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileIncludes
|
||||||
|
}
|
||||||
|
|
||||||
|
// runInterdependAnalyser collects all the included headers names (i.e., dependencies) from each
|
||||||
|
// C/C++ source file of a program and builds an interdependence graph (dot file) between all these
|
||||||
|
// source files.
|
||||||
|
func runInterdependAnalyser(programPath, programName, outFolder string) {
|
||||||
|
|
||||||
|
// Find all program source files
|
||||||
|
sourceFiles, err := findSourcesFiles(getProgramFolder(programPath))
|
||||||
|
if err != nil {
|
||||||
|
u.PrintErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a temporary folder and copy all source files into it for use with the gcc
|
||||||
|
// preprocessor
|
||||||
|
tmpFolder := "tmp/"
|
||||||
|
_, err = u.CreateFolder(tmpFolder)
|
||||||
|
if err != nil {
|
||||||
|
u.PrintErr(err)
|
||||||
|
}
|
||||||
|
var tmpFiles []string
|
||||||
|
for _, sourceFilePath := range sourceFiles {
|
||||||
|
if err := u.CopyFileContents(sourceFilePath,
|
||||||
|
tmpFolder+filepath.Base(sourceFilePath)); err != nil {
|
||||||
|
u.PrintErr(err)
|
||||||
|
}
|
||||||
|
tmpFiles = append(tmpFiles, tmpFolder+filepath.Base(sourceFilePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyse source files include directives and collect header names. Source files are first
|
||||||
|
// analysed "by hand" to get all their include directives and then by gcc to make sure to avoid
|
||||||
|
// directives that are commented or subjected to a macro.
|
||||||
|
interdependMap := make(map[string][]string)
|
||||||
|
for _, tmpFile := range tmpFiles {
|
||||||
|
interdependMap[filepath.Base(tmpFile)] = make([]string, 0)
|
||||||
|
analysis := sourceFileIncludesAnalysis(tmpFile)
|
||||||
|
gccAnalysis := gccSourceFileIncludesAnalysis(tmpFile, tmpFolder)
|
||||||
|
for _, includeDirective := range gccAnalysis {
|
||||||
|
if u.Contains(analysis, includeDirective) {
|
||||||
|
interdependMap[filepath.Base(tmpFile)] =
|
||||||
|
append(interdependMap[filepath.Base(tmpFile)], includeDirective)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create dot file
|
||||||
|
u.GenerateGraph(programName, outFolder+programName, interdependMap, nil)
|
||||||
|
|
||||||
|
// Remove tmp folder
|
||||||
|
u.PrintInfo("Remove folder " + tmpFolder)
|
||||||
|
_ = os.RemoveAll(tmpFolder)
|
||||||
|
}
|
||||||
|
|
||||||
// RunAnalyserTool allows to run the dependency analyser tool.
|
// RunAnalyserTool allows to run the dependency analyser tool.
|
||||||
func RunAnalyserTool(homeDir string, data *u.Data) {
|
func RunAnalyserTool(homeDir string, data *u.Data) {
|
||||||
|
|
||||||
|
@ -95,6 +201,11 @@ func RunAnalyserTool(homeDir string, data *u.Data) {
|
||||||
if *args.BoolArg[fullDepsArg] {
|
if *args.BoolArg[fullDepsArg] {
|
||||||
saveGraph(programName, outFolder, data)
|
saveGraph(programName, outFolder, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create source files interdependence graph if interdependence option is set
|
||||||
|
if *args.BoolArg[interdependArg] {
|
||||||
|
runInterdependAnalyser(programPath, programName, outFolder)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// displayProgramDetails display various information such path, background, ...
|
// displayProgramDetails display various information such path, background, ...
|
||||||
|
@ -157,8 +268,8 @@ func checkElf(programPath *string) (*elf.File, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// runStaticAnalyser runs the static analyser
|
// runStaticAnalyser runs the static analyser
|
||||||
func runStaticAnalyser(elfFile *elf.File, isDynamic, isLinux bool, args *u.Arguments, programName, programPath,
|
func runStaticAnalyser(elfFile *elf.File, isDynamic, isLinux bool, args *u.Arguments, programName,
|
||||||
outFolder string, data *u.Data) {
|
programPath, outFolder string, data *u.Data) {
|
||||||
|
|
||||||
staticAnalyser(elfFile, isDynamic, isLinux, *args, data, programPath)
|
staticAnalyser(elfFile, isDynamic, isLinux, *args, data, programPath)
|
||||||
|
|
||||||
|
@ -228,3 +339,64 @@ func saveGraph(programName, outFolder string, data *u.Data) {
|
||||||
programName+"_shared_libs", data.DynamicData.SharedLibs, nil)
|
programName+"_shared_libs", data.DynamicData.SharedLibs, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// /!\ MISSING "/" !!!
|
||||||
|
stringFile := "#include<stdlib.h>\n/* #include ta mère *\nint main() {\n\t// Salut bitch !\n\treturn 0;\n}"
|
||||||
|
|
||||||
|
for {
|
||||||
|
comStartIndex := strings.Index(stringFile, "/*")
|
||||||
|
if comStartIndex != -1 {
|
||||||
|
comEndIndex := strings.Index(stringFile, "*")
|
||||||
|
stringFile = strings.Join([]string{stringFile[:comStartIndex],
|
||||||
|
stringFile[comEndIndex+2:]}, "")
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//what to do with "\t" in lines ?
|
||||||
|
var finalFile []string
|
||||||
|
sliceFile := strings.Split(stringFile, "\n")
|
||||||
|
for i := 0; i < len(sliceFile); i++ {
|
||||||
|
if !strings.HasPrefix(sliceFile[i], "//") {
|
||||||
|
finalFile = append(finalFile, sliceFile[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove dependencies whose files are not in program directory (e.g., stdio, stdlib, ...)
|
||||||
|
for internalFile, dependencies := range interdependMap {
|
||||||
|
var internalDep []string
|
||||||
|
for _, dependency := range dependencies {
|
||||||
|
if _, ok := interdependMap[dependency]; ok {
|
||||||
|
a++
|
||||||
|
internalDep = append(internalDep, dependency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interdependMap[internalFile] = internalDep
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect and print removable program source files (i.e., files that no other file depends
|
||||||
|
// on)
|
||||||
|
var removableFiles []string
|
||||||
|
for internalFile := range interdependMap {
|
||||||
|
depends := false
|
||||||
|
for _, dependencies := range interdependMap {
|
||||||
|
for _, dependency := range dependencies {
|
||||||
|
if internalFile == dependency {
|
||||||
|
depends = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if depends {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !depends {
|
||||||
|
removableFiles = append(removableFiles, internalFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("Removable program source files of ", programName, ":")
|
||||||
|
fmt.Println(removableFiles)
|
||||||
|
*/
|
||||||
|
|
|
@ -194,6 +194,25 @@ func executeDependAptCache(programName string, data *u.StaticData,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getProgramFolder gets the folder path in which the given program is located, according to the
|
||||||
|
// Unikraft standard (e.g., /home/.../apps/programFolder/.../program).
|
||||||
|
//
|
||||||
|
// It returns the folder containing the program files according to the standard described above.
|
||||||
|
func getProgramFolder(programPath string) string {
|
||||||
|
|
||||||
|
tmp := strings.Split(programPath, "/")
|
||||||
|
i := 2
|
||||||
|
|
||||||
|
for ; i < len(tmp); i++ {
|
||||||
|
if tmp[len(tmp)-i] == "apps" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
folderPath := strings.Join(tmp[:len(tmp)-i+2], "/")
|
||||||
|
return folderPath
|
||||||
|
}
|
||||||
|
|
||||||
// findSourcesFiles puts together all C/C++ source files found in a given application folder.
|
// findSourcesFiles puts together all C/C++ source files found in a given application folder.
|
||||||
//
|
//
|
||||||
// It returns a slice containing the found source file names and an error if any, otherwise it
|
// It returns a slice containing the found source file names and an error if any, otherwise it
|
||||||
|
@ -228,10 +247,7 @@ func findSourcesFiles(workspace string) ([]string, error) {
|
||||||
// it returns nil.
|
// it returns nil.
|
||||||
func ExecuteCommand(command string, arguments []string) (string, error) {
|
func ExecuteCommand(command string, arguments []string) (string, error) {
|
||||||
out, err := exec.Command(command, arguments...).CombinedOutput()
|
out, err := exec.Command(command, arguments...).CombinedOutput()
|
||||||
if err != nil {
|
return string(out), err
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(out), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addSourceFileSymbols adds all the symbols present in 'output' to the static data field in
|
// addSourceFileSymbols adds all the symbols present in 'output' to the static data field in
|
||||||
|
@ -275,21 +291,12 @@ func extractPrototype(sourcesFiltered []string, data *u.Data) error {
|
||||||
// It returns an error if any, otherwise it returns nil.
|
// It returns an error if any, otherwise it returns nil.
|
||||||
func gatherSourceFileSymbols(data *u.Data, programPath string) error {
|
func gatherSourceFileSymbols(data *u.Data, programPath string) error {
|
||||||
|
|
||||||
tmp := strings.Split(programPath, "/")
|
sourceFiles, err := findSourcesFiles(getProgramFolder(programPath))
|
||||||
i := 2
|
|
||||||
for ; i < len(tmp); i++ {
|
|
||||||
if tmp[len(tmp)-i] == "apps" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
folderPath := strings.Join(tmp[:len(tmp)-i+2], "/")
|
|
||||||
|
|
||||||
files, err := findSourcesFiles(folderPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.PrintErr(err)
|
u.PrintErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := extractPrototype(files, data); err != nil {
|
if err := extractPrototype(sourceFiles, data); err != nil {
|
||||||
u.PrintErr(err)
|
u.PrintErr(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Add table
Reference in a new issue