# Copyright (c) 2018-2019 The University of Manchester
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .file_converter import FileConverter
import os
import sys
if 'SPINN_DIRS' in os.environ:
RANGE_DIR = os.path.join(os.environ['SPINN_DIRS'], "lib")
else:
RANGE_DIR = "lib"
DICTIONARY_HEADER = "Id,Preface,Original\n" \
",DO NOT EDIT,THIS FILE WAS AUTOGENERATED BY MAKE\n"
RANGE_PER_DIR = 1000
SKIPABLE_FILES = ["common.mk", "Makefile.common",
"paths.mk", "Makefile.paths",
"neural_build.mk", "Makefile.neural_build"]
LOG_RANGE_FILENAME = "log.ranges"
LOG_FILE_HEADER = "AUTOGENERATED DO NOT EDIT\n"
[docs]class Converter(object):
__slots__ = [
# Full destination directory
"_dest",
# Part of destination directory to replace in when converting paths
"_dest_basename",
# File to hold dictionary mappings
"_dict",
# Full source directory
"_src",
# Part of source directory to take out when converting paths
"_src_basename"]
def __init__(self, src, dest, dict_file):
""" Converts a whole directory including sub directories
:param src: Full source directory
:type src: str
:param dest: Full destination directory
:type dest: str
:param dict_file: Full path to dictionary file
:type dict_file: str
"""
self._src = os.path.abspath(src)
if not os.path.exists(self._src):
raise Exception(
"Unable to locate source directory {}".format(self._src))
self._dest = os.path.abspath(dest)
src_root, src_basename = os.path.split(
os.path.normpath(self._src))
dest_root, dest_basename = os.path.split(
os.path.normpath(self._dest))
if src_root != dest_root:
# They must be siblings due to text manipulation in makefiles
raise Exception("src and destination must be siblings")
self._src_basename = src_basename
self._dest_basename = dest_basename
self._dict = os.path.abspath(dict_file)
[docs] def run(self):
""" Runs the file converter on a whole directory including sub \
directories
WARNING. This code is absolutely not thread safe.
Interwoven calls even on different FileConverter objects is dangerous!
It is highly likely that dict files become corrupted and the same
message_id is used multiple times.
:return:
"""
self._mkdir(self._dest)
with open(self._dict, 'w') as dict_f:
dict_f.write(DICTIONARY_HEADER)
message_id = self._get_id()
for dir_name, _subdir_list, file_list in os.walk(self._src):
self._mkdir(dir_name)
for file_name in file_list:
_, extension = os.path.splitext(file_name)
source = os.path.join(dir_name, file_name)
if extension in [".c", ".cpp", ".h"]:
destination = self._any_destination(source)
message_id = FileConverter.convert(
source, destination, self._dict, message_id)
elif file_name in SKIPABLE_FILES:
pass
else:
print("Unexpected file {}".format(source))
def _get_id(self):
rangefile = os.path.join(RANGE_DIR, LOG_RANGE_FILENAME)
range_start = 0
filename = self._dest
common_dir = self._find_common_based_on_environ()
if filename.startswith(common_dir):
filename = filename[len(common_dir)+1:]
# If the range_file does not exist create it and use range_start
if not os.path.exists(rangefile):
with open(rangefile, 'w') as log_ranges_file:
log_ranges_file.write(LOG_FILE_HEADER)
log_ranges_file.write("{} {}\n".format(
range_start, filename))
return range_start
# Check if the file is ranged or find highest range so far
highest_found = range_start
with open(rangefile, 'r') as log_ranges_file:
data_lines = iter(log_ranges_file)
next(data_lines) # Ignore do not edit
for line in data_lines:
parts = line.split(" ", 1)
if filename.strip() == parts[1].strip():
return int(parts[0])
else:
highest_found = max(highest_found, int(parts[0]))
# Go one step above best found
new_start = highest_found + RANGE_PER_DIR
# Append to range file in case rebuilt without clean
with open(rangefile, 'a') as log_ranges_file:
log_ranges_file.write("{} {}\n".format(new_start, filename))
return new_start
def _any_destination(self, path):
# Here we need the local separator
src_bit = os.path.sep + self._src_basename + os.path.sep
dest_bit = os.path.sep + self._dest_basename + os.path.sep
li = path.rsplit(src_bit, 1)
return dest_bit.join(li)
def _mkdir(self, destination):
if not os.path.exists(destination):
os.mkdir(destination)
if not os.path.exists(destination):
raise Exception("mkdir failed {}".format(destination))
def _find_common_based_on_environ(self):
if 'SPINN_DIRS' not in os.environ:
return ""
if 'NEURAL_MODELLING_DIRS' not in os.environ:
return ""
return os.path.dirname(os.path.commonprefix(
[os.environ['SPINN_DIRS'], os.environ['NEURAL_MODELLING_DIRS']]))
[docs] @staticmethod
def convert(src, dest, dict_file):
converter = Converter(src, dest, dict_file)
converter.run()
if __name__ == '__main__':
_src = sys.argv[1]
_dest = sys.argv[2]
_dict_file = sys.argv[3]
Converter.convert(_src, _dest, _dict_file)