blob: 47fc75b3ea5899b931211800212309118f8204f7 [file] [log] [blame]
#!/usr/bin/python
"""Parses a 'git diff --stat' file to extract what files have been changed as
shown in the diff. Extracts the directory paths of those files, and decides
which components of AKAROS should be compiled and tested, accordingly.
"""
import json
import os
import re
import sys
REGEX_EXTRACT_PATH_FROM_GIT_DIFF_LINE = r'^(?:\s)*([^\s]*)(?:\s)*\|(?:.*)\n?$'
# Path to file with git diff
DIFF_FILE = sys.argv[1]
# Path to file with JSON definition of akaros components
CONFIG_COMP_COMPONENTS_FILE = sys.argv[2]
# Arguments to fill variable paths with. Useful, for example, to define a path
# that will vary whether we are compiling an architecture or another.
# TODO(alfongj): Get these from Env var or sys.argv
"""The following is the definition of the given components of AKAROS (filled in
from CONFIG_COMP_COMPONENTS_FILE (which should be
config/compilation_components.json or something).
Each 'component' consists of a name (which will be a unique identifier printed
out to console) as key, plus a list of PATHS as content. If any of these paths
is modified, then we will consider that the full component is affected, and
therefore may need to be compiled and tested again.
Paths should be written from the root repo folder, beginning with './' and not
'/', and ending in '/'.
If a path should include any subpaths, then it must be appended with a + symbol.
e.g. ./path/with/subpaths/+
If a path has variable arguments, they should be represented like {{this}}.
These arguments will be filled in with the contents of PATH_ARGS.
e.g. ./path/with/{{VARIABLE}}/{{ARGUMENTS}}/.
"""
akaros_components = {}
affected_components = {}
def get_variable_path_args() :
"""Returns dict of arguments to use in the load_component_config function
to generate dynamic paths. Currently it is only being used to change a
subdirectory in one of the paths depending on the architecture being
tested.
"""
PATH_ARGS = {
"I686": {
"arch": "x86"
},
"X86_64": {
"arch": "x86"
},
"RISCV": {
"arch": "riscv"
}
}
compilation_arch = os.getenv('COMPILATION_ARCH', 'I686')
return PATH_ARGS[compilation_arch]
def load_component_config() :
"""Loads ../config/compilation_components.json object, which contains a
list of all the different AKAROS compilation components along with the paths
to look for for compiling them.
"""
conf_file_contents = ""
# Read config file.
with open(CONFIG_COMP_COMPONENTS_FILE, 'r') as conf_file :
conf_file_contents = conf_file.read().replace('\n', '')
# Replace variable arguments.
var_path_args = get_variable_path_args()
for arg in var_path_args :
wrapped_arg = "{{" + arg + "}}"
conf_file_contents = conf_file_contents.replace(wrapped_arg,
var_path_args[arg])
# Parse JSON into python object.
global akaros_components
akaros_components = json.loads(conf_file_contents)['compilation_components']
def extract_dir(diff_line) :
"""Given a line from a "git diff --stat" output, it tries to extract a
directory from it.
If a blank or non-change line is passed, it ignores it and returns nothing.
If a 'change' line (e.g. ' path/to/file.ext | 33++ ') is passed, it strips
the path (not including the file name) and prepends a './' to it and returns
it.
"""
match = re.match(REGEX_EXTRACT_PATH_FROM_GIT_DIFF_LINE, diff_line)
if (match) :
full_path = './' + match.group(1)
folder_list = full_path.split('/')[0:-1]
folder_path = '/'.join(folder_list) + '/'
return folder_path
def includes_subpaths(path) :
"""Checks if a given path includes subpaths or not. It includes them if it
ends in a '+' symbol.
"""
return path[-1] == '+'
def check_components_affected(path_of_changed_file) :
"""Checks if a given directory should set the state of one of the components
to affected.
"""
global affected_components
for component in akaros_components :
affected = component in affected_components
paths = akaros_components[component]['PATHS']
if (not affected) :
for path in paths :
if (includes_subpaths(path)) :
# Checks if a given string contains the given path.
# e.g., If the path is 'path/to':
# The regex will match for: 'path/to/file.txt' or
# 'path/to/and/subpath/to/file.txt'
# But not for: 'path/file.txt' nor for 'path/tofile.txt' or
# 'path/tobananas/file.txt'
regex = re.compile('^\%s(?:.*/)*$' % path[:-1])
else :
# Checks if a given string contains the given path with no
# subpaths.
# e.g., If the path is 'path/to':
# The regex will match for: 'path/to/file.txt'
# But not for: 'path/file.txt' nor for 'path/tofile.txt' or
# 'path/tobananas/file.txt' or
# 'path/to/and/subpath/to/file.txt'
regex = re.compile('^\%s[^/]*$' % path)
if (re.match(regex, path_of_changed_file)) :
affected_components[component] = True
break
def main() :
load_component_config()
diff_file = open(DIFF_FILE)
for line in diff_file :
cur_dir = extract_dir(line)
if (cur_dir) :
check_components_affected(cur_dir)
print ' '.join(affected_components)
main()