| #!/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() |