# 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 str src: Full source directory
:param str dest: Full destination directory
:param str dict_file: Full path to dictionary file
"""
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.
"""
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):
""" Wrapper function around this class.
"""
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)