From ac62e6e13499bade225b9893d159c01c517679d1 Mon Sep 17 00:00:00 2001 From: Tom Close Date: Sat, 28 Jan 2017 10:54:19 +1100 Subject: [PATCH] added loading of saved paths to utilities --- paths/README | 15 +++++++ pype9/base/cells/code_gen.py | 57 ++++++++++++++++--------- pype9/nest/cells/code_gen/__init__.py | 2 +- pype9/neuron/cells/code_gen/__init__.py | 6 +-- setup.py | 52 +++++++++++++--------- 5 files changed, 86 insertions(+), 46 deletions(-) create mode 100644 paths/README diff --git a/paths/README b/paths/README new file mode 100644 index 00000000..f08bcd5b --- /dev/null +++ b/paths/README @@ -0,0 +1,15 @@ +The 'paths' directory contains the paths of utilities required in the code +generation process. They should be automatically generated by the installation +process but can be manually updated at a later stage if required. + +The paths that can be set in this directory (as the contents of plain text +files with '_path' appended to the file name e.g. 'nrnivmodl_path') are: + + * nrnivmodl + * modlunit + * nest-config + +If a path file is not provided then the PATH environment variable will be +searched for the utility. Note that in this case it is important to double +check that the libninemlnrn.so is compiled with the same C compiler as the +version of NEURON the nrnivmodl on the PATH refers to. diff --git a/pype9/base/cells/code_gen.py b/pype9/base/cells/code_gen.py index 95cdf262..11d49bac 100644 --- a/pype9/base/cells/code_gen.py +++ b/pype9/base/cells/code_gen.py @@ -299,32 +299,47 @@ def render_to_file(self, template, args, filename, directory, switches={}, with open(os.path.join(directory, filename), 'w') as f: f.write(contents) - def path_to_exec(self, exec_name): + def path_to_utility(self, utility_name): """ Returns the full path to an executable by searching the "PATH" environment variable - `exec_name` [str] -- Name of executable to search the execution path - return [str] -- Full path to executable + Parameters + ---------- + utility_name : str + Name of executable to search the execution path + + Returns + ------- + utility_path : str + Full path to executable """ - if platform.system() == 'Windows': - exec_name += '.exe' - # Get the system path - system_path = os.environ['PATH'].split(os.pathsep) - # Append NEST_INSTALL_DIR/NRNHOME if present - system_path.extend(self.simulator_specific_paths()) - # Check the system path for the command - exec_path = None - for dr in system_path: - path = join(dr, exec_name) - if os.path.exists(path): - exec_path = path - break - if not exec_path: - raise Pype9BuildError( - "Could not find executable '{}' on the system path '{}'" - .format(exec_name, ':'.join(system_path))) - return exec_path + # Check to see whether the path of the utility has been saved in the + # 'paths' directory (typically during installation) + saved_path_path = os.path.join(os.path.dirname(pype9.__file__), + 'paths', utility_name + '_path') + try: + with open(saved_path_path) as f: + utility_path = f.read() + except OSError: + if platform.system() == 'Windows': + utility_name += '.exe' + # Get the system path + system_path = os.environ['PATH'].split(os.pathsep) + # Append NEST_INSTALL_DIR/NRNHOME if present + system_path.extend(self.simulator_specific_paths()) + # Check the system path for the command + utility_path = None + for dr in system_path: + path = join(dr, utility_name) + if os.path.exists(path): + utility_path = path + break + if not utility_path: + raise Pype9BuildError( + "Could not find executable '{}' on the system path '{}'" + .format(utility_name, ':'.join(system_path))) + return utility_path def simulator_specific_paths(self): """ diff --git a/pype9/nest/cells/code_gen/__init__.py b/pype9/nest/cells/code_gen/__init__.py index c77edc1a..8e304b9b 100644 --- a/pype9/nest/cells/code_gen/__init__.py +++ b/pype9/nest/cells/code_gen/__init__.py @@ -52,7 +52,7 @@ class CodeGenerator(BaseCodeGenerator): def __init__(self, build_cores=1): super(CodeGenerator, self).__init__() self._build_cores = build_cores - nest_config = self.path_to_exec('nest-config') + nest_config = self.path_to_utility('nest-config') compiler = sp.check_output('{} --compiler'.format(nest_config), shell=True) self._compiler = compiler[:-1] # strip trailing \n diff --git a/pype9/neuron/cells/code_gen/__init__.py b/pype9/neuron/cells/code_gen/__init__.py index 0f884175..5f7f7a4d 100644 --- a/pype9/neuron/cells/code_gen/__init__.py +++ b/pype9/neuron/cells/code_gen/__init__.py @@ -69,8 +69,8 @@ class CodeGenerator(BaseCodeGenerator): def __init__(self, gsl_path=None): super(CodeGenerator, self).__init__() - self.nrnivmodl_path = self.path_to_exec('nrnivmodl') - self.modlunit_path = self.path_to_exec('modlunit') + self.nrnivmodl_path = self.path_to_utility('nrnivmodl') + self.modlunit_path = self.path_to_utility('modlunit') self.nrnivmodl_flags = [ '-L' + self.LIBNINEMLNRN_PATH, '-Wl,-rpath,' + self.LIBNINEMLNRN_PATH, @@ -81,7 +81,7 @@ def __init__(self, gsl_path=None): try: # Check nest-config (if installed) to get any paths needed for # gsl - nest_config_path = self.path_to_exec('nest-config') + nest_config_path = self.path_to_utility('nest-config') nest_lflags = sp.Popen( [nest_config_path, '--libs'], stdout=sp.PIPE).communicate()[0].split() diff --git a/setup.py b/setup.py index f79c2058..6a1a7290 100644 --- a/setup.py +++ b/setup.py @@ -39,49 +39,54 @@ class build(_build): def run(self): _build.run(self) + # Get directory of package to be installed package_dir = os.path.join(os.getcwd(), self.build_lib, 'pype9') - # Save path to nrnivmodl and nest-config in hidden files for future - # reference while building generated code + # Get locations of utilities required for the build process (must be + # on the system PATH self.nrnivmodl_path = self.path_to_exec('nrnivmodl') - neuron_code_gen_dir = os.path.join(package_dir, 'neuron', 'cells', - 'code_gen') - with open(os.path.join(neuron_code_gen_dir, - '.nrnivmodlpath'), 'w') as f: - f.write(self.nrnivmodl_path) self.nest_config_path = self.path_to_exec('nest-config') - nest_code_gen_dir = os.path.join(package_dir, 'nest', 'cells', - 'code_gen') - with open(os.path.join(nest_code_gen_dir, - '.nestconfigpath'), 'w') as f: - f.write(self.nest_config_path) # Complie libninemlnrn (for random distribution support in generated - # NEURON mechanisms) + # NMODL mechanisms) print("Attempting to build libninemlnrn") - libninemlnrn_dir = os.path.join(neuron_code_gen_dir, 'libninemlnrn') + libninemlnrn_dir = os.path.join( + package_dir, 'neuron', 'cells', 'code_gen', 'libninemlnrn') try: cc = self.get_nrn_cc() gsl_prefixes = self.get_gsl_prefixes() - echo_cmd = 'pwd; ls' + # Compile libninemlnrn compile_cmd = '{} -fPIC -c -o nineml.o nineml.cpp {}'.format( cc, ' '.join('-I{}/include'.format(p) for p in gsl_prefixes)) + self.run_cmd( + compile_cmd, work_dir=libninemlnrn_dir, + fail_msg=("Unable to compile libninemlnrn extensions")) + # Link libninemlnrn if platform.system() == 'Darwin': + # On macOS '-install_name' option needs to be set to allow + # rpath to find the compiled library install_name = "-install_name @rpath/libninemlnrn.so " else: install_name = "" link_cmd = ( - "{} -shared {} {}" - "-lm -lgslcblas -lgsl -o libninemlnrn.so nineml.o -lc".format( + "{} -shared {} {} -lm -lgslcblas -lgsl " + "-o libninemlnrn.so nineml.o -lc".format( cc, ' '.join('-L{}/lib'.format(p) for p in gsl_prefixes), install_name)) - for cmd in (echo_cmd, compile_cmd, link_cmd): - self.run_cmd( - cmd, work_dir=libninemlnrn_dir, - fail_msg=("Unable to compile libninemlnrn extensions")) + self.run_cmd( + link_cmd, work_dir=libninemlnrn_dir, + fail_msg=("Unable to link libninemlnrn extensions")) print("Successfully compiled libninemlnrn extension.") except CouldNotCompileNRNRandDistrException as e: print("WARNING! Unable to compile libninemlnrn: " "random distributions in NMODL files will not work:\n{}" .format(e)) + # Save paths to utilities to be referenced when building generated code + self.write_path('nest-config', self.nest_config_path) + self.write_path('nrnivmodl', self.nrnivmodl_path) + # Try to save the path of modlunit + try: + self.write_path('modlunit', self.path_to_exec('modlunit')) + except CouldNotCompileNRNRandDistrException: + pass # Not actually required but included in built for add. check def run_cmd(self, cmd, work_dir, fail_msg): p = sp.Popen(cmd, shell=True, stdin=sp.PIPE, stdout=sp.PIPE, @@ -212,6 +217,11 @@ def path_to_exec(self, exec_name): .format(exec_name, ':'.join(system_path))) return exec_path + def write_path(self, name, path): + with open(os.path.join(self.package_dir, 'paths', + name + '_path'), 'w') as f: + f.write(path) + setup( name="pype9",