def _run_compile(self, identifier, testid): """Compiles the executable that was created for the specified identifier, returns True if the compile was successful.""" #Because we often run the tests for multiple compiler versions, we need #a copy of the execution directory that was setup for the testing. from fortpy.testing.compilers import replace, executor, family from fortpy.utility import copytree from os import path source = path.join(self.libraryroot(identifier), identifier) target = replace(source + ".[c]", self.compiler) copytree(source, target) #Before we compile, we need to make sure we have the fortpy.o and fortpy.mod #files for the specific compiler. tversion = self.template_version("fortpy.f90") for sdfile in ["fortpy.o", "fortpy.mod"]: fdfile = replace(sdfile + ".[c]", self.compiler) ftarget = path.join(target, sdfile) dversion = self.get_fortpy_version(ftarget) if not path.isfile(ftarget) or dversion != tversion: from shutil import copy source = path.join(self.fortpy_templates, fdfile) sversion = self.get_fortpy_version(source) if not path.isfile(source) or sversion != tversion: self._compile_fortpyf90(tversion) msg.info(" COPY: {}".format(source)) copy(source, ftarget) #If the file is a binary, we need to save a .v with version #information as well for the next time we want to copy it. pre, ext = path.splitext(ftarget) if ext in [".o", ".so", ".mod"]: with open(ftarget + '.v', 'w') as f: f.write("# <fortpy version=\"{}\" />".format('.'.join(map(str, tversion)))) #Find the target folder that has the executables etc then run #make and check the exit code. msg.blank() options = "" if self.debug: options += " DEBUG=true" if self.profile: options += " GPROF=true" codestr = "cd {}; make -f 'Makefile.{}' F90='{}' FAM='{}'" + options #If we are running in quiet mode, we don't want the compile information #to post to stdout; only errors should be redirected. This means we need #to wrap the execution in a subprocess and redirect the std* to PIPE from os import waitpid from subprocess import Popen, PIPE command = codestr.format(target, testid, executor(self.compiler), family(self.compiler)) pcompile = Popen(command, shell=True, executable="/bin/bash", stdout=PIPE, stderr=PIPE) waitpid(pcompile.pid, 0) if not self.quiet: output = [x.decode('utf8') for x in pcompile.stdout.readlines()] msg.std(''.join(output)) #else: #We don't need to get these lines since we are purposefully redirecting them. error = pcompile.stderr.readlines() code = len(error) if code != 0: msg.err(''.join(error)) #It turns out that the compiler still returns a code of zero, even if the compile #failed because the actual compiler didn't fail; it did its job properly. We need to #check for the existence of errors in the 'compile.log' file. lcount = 0 errors = [] log = path.join(target, "compile.log") with open(log) as f: for line in f: lcount += 1 if lcount > 21 and lcount < 32: errors.append(line) elif lcount > 21: break if len(errors) > 0: #There are 21 lines in the compile.log file when everything runs correctly #Overwrite code with a bad exit value since we have some other problems. code = 1 #We want to write the first couple of errors to the console and give them the #option to still execute if the compile only generated warnings. msg.warn("compile generated some errors or warnings:") msg.blank() msg.info(''.join(errors)) #If the executable exists, we could still prompt them to run it (in case the #additional lines were just warnings). exe = path.join(target, "{}.x".format(testid)) if path.isfile(exe): choice = input("\nWould you still like to run the executable? ").lower() code = 0 if "y" in choice else code if "n" in choice: msg.err("Unit testing terminated by user.") exit(0) else: msg.err("Could not compile executable {}.x".format(testid)) exit(-1) return code == 0, target
def _compile_fortpyf90(self, tversion): """Compiles a fortpy.mod and fortpy.o for the current compiler. """ msg.info("Compiling fortpy.mod and fortpy.f90 for {}".format(self.compiler)) from os import waitpid, path from subprocess import Popen, PIPE from fortpy.testing.compilers import executor, replace command = "cd {0}; {1} fortpy.f90; {1} -c fortpy.f90".format(self.fortpy_templates, executor(self.compiler)) pcompile = Popen(command, shell=True, executable="/bin/bash", stdout=PIPE, stderr=PIPE) waitpid(pcompile.pid, 0) opath = path.join(self.fortpy_templates, "fortpy.o") mpath = path.join(self.fortpy_templates, "fortpy.mod") if path.isfile(opath) and path.isfile(mpath): from shutil import move nopath = path.join(self.fortpy_templates, replace("fortpy.o.[c]", self.compiler)) nmpath = path.join(self.fortpy_templates, replace("fortpy.mod.[c]", self.compiler)) move(opath, nopath) move(mpath, nmpath) #Create the version files so we can keep track of the compiled versions. vpaths = [nopath + ".v", nmpath + ".v"] for vp in vpaths: with open(vp, 'w') as f: f.write('#<fortpy version="{}" />'.format('.'.join(map(str, tversion)))) else: msg.err("Unable to generate fortpy.o and fortpy.mod.")