|
|
|
# Copyright [email protected] 2017 |
|
# Copyright Mathew Topper 2020-2021 |
|
# |
|
# 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 <https://www.gnu.org/licenses/>. |
|
|
|
import os |
|
import glob |
|
import sys |
|
import shutil |
|
import subprocess |
|
import itertools |
|
|
|
if sys.version_info < (3, 0, 0): |
|
# To avoid problems with encoding: |
|
# - Use codecs.open instead of open (Python 2.x only) |
|
# - open files with encoding='utf-8' (Both Python 2.x and 3.x) |
|
# - Do not use str(line) on lines read from file |
|
from codecs import open as open |
|
import _winreg as winreg |
|
else: |
|
import winreg |
|
|
|
|
|
# toolsversion specifies the vs tools version |
|
toolsversion = {} |
|
toolsversion[2010] = "4.0" |
|
toolsversion[2012] = "4.0" |
|
toolsversion[2013] = "12.0" |
|
toolsversion[2014] = "12.0" |
|
toolsversion[2015] = "14.0" |
|
toolsversion[2016] = "14.0" |
|
toolsversion[2017] = "15.0" |
|
toolsversion[2019] = "15.0" |
|
|
|
# platformtoolset specifies the vs platform toolset version |
|
platformtoolset = {} |
|
platformtoolset[2010] = "" |
|
platformtoolset[2012] = "v110" |
|
platformtoolset[2013] = "v120" |
|
platformtoolset[2014] = "v120" |
|
platformtoolset[2015] = "v140" |
|
platformtoolset[2016] = "v140" |
|
platformtoolset[2017] = "v141" |
|
platformtoolset[2019] = "v142" |
|
|
|
|
|
def subkeys(path, hkey=winreg.HKEY_LOCAL_MACHINE, flags=0): |
|
with winreg.OpenKey(hkey, path, 0, winreg.KEY_READ|flags) as k: |
|
try: |
|
for i in itertools.count(): |
|
yield winreg.EnumKey(k, i) |
|
except WindowsError: |
|
pass |
|
|
|
|
|
def get_oneAPI_compilers_major_version(): |
|
|
|
key = "SOFTWARE\\WOW6432Node\\Intel\\1AProducts\\Compilers" |
|
versions = [] |
|
|
|
for subkey in subkeys(key): |
|
|
|
REG_PATH = key + "\\" + subkey |
|
registry_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, |
|
REG_PATH, |
|
0, |
|
winreg.KEY_READ) |
|
majorvalue, regtype = winreg.QueryValueEx(registry_key, "MajorVersion") |
|
versions.append(majorvalue) |
|
|
|
if not versions: |
|
raise ValueError("Intel oneAPI not found") |
|
|
|
return max(versions) |
|
|
|
|
|
def get_vs_version(): |
|
|
|
cmd = '"%ProgramFiles(x86)%\\Microsoft Visual Studio\\Installer\\vswhere.exe"' |
|
result = subprocess.check_output(cmd, shell=True) |
|
lines = result.split(b"\n") |
|
for line in lines: |
|
words = line.split(b":") |
|
if words[0] == b"catalog_productLineVersion": |
|
return int(words[1]) |
|
raise ValueError("Visual Studio not found") |
|
|
|
|
|
def get_net_version(): |
|
|
|
folder = ("C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\" |
|
"Framework\\.NETFramework") |
|
|
|
try: |
|
dirlist = [item for item in os.listdir(folder) if |
|
os.path.isdir(os.path.join(folder, item))] |
|
except WindowsError: |
|
dirlist = [] |
|
|
|
if not dirlist: |
|
raise ValueError(".NET Framework Pack not found") |
|
|
|
if "v4.X" in dirlist: dirlist.remove("v4.X") |
|
|
|
return sorted(dirlist)[-1] |
|
|
|
|
|
def getUCRTSDKdir(vs): |
|
|
|
vsintalldir = "VS{}INSTALLDIR".format(vs) |
|
getucrtdir = ( |
|
'"' |
|
+ str(os.environ.get(vsintalldir)) |
|
+ "\\VC\\Auxiliary\\Build\\vcvarsall.bat" |
|
+ '" amd64&&set UniversalCRTSdkDir' |
|
) |
|
|
|
sys.stdout.write("Trying to execute: " + getucrtdir + " ...\n") |
|
|
|
try: |
|
result = subprocess.check_output(getucrtdir, shell=True) |
|
except Exception as e: |
|
result = "" |
|
sys.stdout.write( |
|
"\n\n *** ERROR: Execution failed; is VisualStudio " |
|
+ str(vs) |
|
+ " installed?\n" |
|
) |
|
sys.stdout.write(" *** Python reported: " + str(e) + "\n\n") |
|
|
|
# If python 2, decode to string |
|
try: |
|
result = result.decode("utf-8") |
|
except AttributeError: |
|
pass |
|
|
|
ucrtpos = result.rfind("UniversalCRTSdkDir=") |
|
|
|
if ucrtpos == -1: |
|
# Fallback: it should be this: |
|
sys.stdout.write("ucrtdir not found; set to default value\n") |
|
ucrtdir = "c:\\Program Files (x86)\\Windows Kits\\10\\Lib\\" |
|
else: |
|
ucrtdir = result[ucrtpos + 19 :] |
|
# Remove the trailing slash and the newline-character behind it |
|
lastslash = ucrtdir.rfind("\\") |
|
if lastslash != -1: |
|
ucrtdir = ucrtdir[:lastslash] |
|
sys.stdout.write("ucrtdir found: " + ucrtdir + "\n") |
|
|
|
return ucrtdir |
|
|
|
|
|
def getUCRTlibdir(vs): |
|
|
|
ucrtlibdir = {} |
|
ucrtlibtemplate = "$(UniversalCRTSdkDir)Lib\\{}\\ucrt\\{}" |
|
|
|
# Only for VS2015 or higher |
|
if vs < 2015: |
|
return "$(OSS_UCRTLIBDIR)" |
|
|
|
# To get the ucrt directory: execute the matching getucrtdir string, |
|
# catch the stdout of that command, |
|
# check whether UniversalCRTSdkDir is in that string, |
|
# if so, get the value behind the '='-sign |
|
ucrtdir = getUCRTSDKdir(vs) |
|
|
|
# Search in subdir Lib for directories starting with a digit and |
|
# containing at least one "." |
|
searchstring = os.path.join(ucrtdir, "Lib", "[0-9]*.*") |
|
versions = glob.glob(searchstring) |
|
if len(versions) <= 0: |
|
# Fallback: it should be this: |
|
ucrtversion = "10.0.10586.0" |
|
sys.stdout.write( |
|
"No versions found, using default version:" + ucrtversion + "\n" |
|
) |
|
else: |
|
# Choose the highest version number |
|
versions.sort(reverse=True) |
|
ucrtversion = versions[0] |
|
ucrtversion = os.path.basename(ucrtversion) |
|
sys.stdout.write("Versions found, using: " + ucrtversion + "\n") |
|
|
|
# Inside ucrtlibdir, replace all occurences of UCRTLIBDIRVERSIONNUMBER by |
|
# ucrtversion |
|
ucrtlibdir[32] = str(ucrtlibtemplate).format(ucrtversion, "x86") |
|
ucrtlibdir[64] = str(ucrtlibtemplate).format(ucrtversion, "x64") |
|
|
|
return ucrtlibdir |
|
|
|
|
|
# process_solution_file ==================================== |
|
# process a VisualStudio Solution File (and underlying projects) |
|
# Pass only file names, no full path names. It assumed that both |
|
# files are in fixed locations (see below). |
|
def process_solution_file(sln, slntemplate, vs, fw, oa, ucrtlibdir): |
|
|
|
# Copy the solution template file to the solution file |
|
sys.stdout.write("Creating file " + sln + " ...\n") |
|
scriptdir = os.path.dirname(os.path.abspath(__file__)) |
|
topdir = scriptdir |
|
|
|
# target file: |
|
sln = os.path.join(topdir, sln) |
|
|
|
# source template file: |
|
slntemplate = os.path.join(topdir, slntemplate) |
|
|
|
shutil.copyfile(slntemplate, sln) |
|
|
|
# Collect the project files referenced in the solution file |
|
projectfiles = [] |
|
# Read sln file: |
|
# Put the full file contents in filin_contents |
|
with open(sln, "r", encoding="utf-8") as filinhandle: |
|
filin_contents = filinhandle.readlines() |
|
|
|
# Scan the contents and rewrite the full solution file |
|
with open(sln, "w", encoding="utf-8") as filouthandle: |
|
for line in filin_contents: |
|
# Search for project file references |
|
pp = line.split('"') |
|
for subline in pp: |
|
if ( |
|
max( |
|
subline.find(".vfproj"), |
|
subline.find(".vcxproj"), |
|
subline.find(".vcproj"), |
|
subline.find(".csproj"), |
|
) |
|
!= -1 |
|
): |
|
projectfiles.append(subline) |
|
# Changes to the sln file based on VS version |
|
startpos = line.find( |
|
"Microsoft Visual Studio Solution File, Format Version" |
|
) |
|
if startpos == 0: |
|
if vs == 2010: |
|
line = "Microsoft Visual Studio Solution File, Format Version 11.00\r\n" |
|
elif vs == 2012: |
|
line = "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" |
|
elif vs == 2013: |
|
line = "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" |
|
elif vs == 2014: |
|
line = "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" |
|
elif vs == 2015: |
|
line = "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" |
|
elif vs == 2016: |
|
line = "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" |
|
elif vs == 2017: |
|
line = "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" |
|
elif vs == 2019: |
|
line = "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" |
|
# else: |
|
# leave line unchanged |
|
startpos = line.find("# Visual Studio") |
|
if startpos == 0: |
|
if vs == 2010: |
|
line = "# Visual Studio 2010\r\n" |
|
elif vs == 2012: |
|
line = "# Visual Studio 2012\r\n" |
|
elif vs == 2013: |
|
line = "# Visual Studio 2013\r\n" |
|
elif vs == 2014: |
|
line = "# Visual Studio 2014\r\n" |
|
elif vs == 2015: |
|
line = "# Visual Studio 2015\r\n" |
|
elif vs == 2016: |
|
line = "# Visual Studio 2016\r\n" |
|
elif vs == 2017: |
|
line = "# Visual Studio 2017\r\n" |
|
elif vs == 2019: |
|
line = "# Visual Studio 2019\r\n" |
|
# else: |
|
# leave line unchanged |
|
filouthandle.write(line) |
|
|
|
# Process all project files referenced in the sln file |
|
for pfile in projectfiles: |
|
pfile = os.path.join(topdir, pfile) |
|
sys.stdout.write("Processing file " + pfile + " ...\n") |
|
if os.path.isfile(pfile): |
|
process_project_file(pfile, vs, fw, oa, ucrtlibdir) |
|
else: |
|
sys.stdout.write("ERROR: File does not exists:" + pfile + "\n") |
|
sys.stdout.write("...Finished.\n") |
|
sys.stdout.write('Ready to be used: "' + sln + '"\n') |
|
|
|
# process_project_file ==================================== |
|
# process a VisualStudio Project File |
|
def process_project_file(pfile, |
|
vs, |
|
fw, |
|
oa, |
|
ucrtlibdir, |
|
remove_petsc=True): |
|
|
|
# Type (F/C/C#) and related flags are set based on the file extension |
|
ptype = "unknown" |
|
config_tag = "unknown" |
|
config_val32 = "unknown" |
|
config_val64 = "unknown" |
|
compilervar = "$(IFORT_COMPILER{:02})".format(oa % 100) |
|
oneapivar = "$(ONEAPI_ROOT)" |
|
|
|
if pfile.find("vfproj") != -1: |
|
ptype = "fortran" |
|
config_tag = "Configuration Name=" |
|
config_val32 = "Win32" |
|
config_val64 = "x64" |
|
redistdir = compilervar + "redist\\{}\\compiler\\"" |
|
mkldir = oneapivar + "mkl\\latest\\redist\\{}\\"" |
|
libdir = compilervar + "\\compiler\\lib\\{}" |
|
elif pfile.find("vcxproj") != -1: |
|
ptype = "c" |
|
config_tag = "ItemDefinitionGroup Condition=" |
|
config_val32 = "Win32" |
|
config_val64 = "x64" |
|
redistdir = compilervar + "redist\\{}\\compiler\\" |
|
mkldir = oneapivar + "mkl\\latest\\redist\\{}\\" |
|
libdir = compilervar + "\\compiler\\lib\\{}" |
|
elif pfile.find("vcproj") != -1 or pfile.find("csproj") != -1: |
|
ptype = "csharp" |
|
config_tag = "PropertyGroup Condition=" |
|
config_val32 = "x86" |
|
config_val64 = "x64" |
|
|
|
# Put the full file contents in filin_contents |
|
with open(pfile, "r", encoding="utf-8") as filinhandle: |
|
filin_contents = filinhandle.readlines() |
|
|
|
# Scan the contents and rewrite the full file |
|
configuration = 0 |
|
with open(pfile, "w", encoding="utf-8") as filouthandle: |
|
|
|
fixing_petsc = False |
|
|
|
for line in filin_contents: |
|
|
|
# Remove petsc source file |
|
if remove_petsc: |
|
if 'solve_petsc.F90' in line: |
|
fixing_petsc = True |
|
|
|
if fixing_petsc: |
|
startpos = line.find( |
|
'<FileConfiguration Name="Release|x64"') |
|
if startpos != -1: |
|
line = line[:startpos + 37] + \ |
|
' ExcludedFromBuild="true">\n' |
|
fixing_petsc = False |
|
|
|
# |
|
# ToolsVersion |
|
# Skip this change when vs=0 |
|
if vs != 0: |
|
startpos = line.find("ToolsVersion=") |
|
if startpos != -1: |
|
parts = line.split('"') |
|
i = 0 |
|
for part in parts: |
|
if part.find("ToolsVersion=") != -1: |
|
parts[i + 1] = toolsversion[vs] |
|
i += 1 |
|
line = '"'.join(parts) |
|
|
|
# FrameworkVersion |
|
# Skip this change when fw=0 |
|
if fw != 0: |
|
startpos = line.find("<TargetFrameworkVersion>") |
|
|
|
if startpos != -1: |
|
endpos = line.find("</TargetFrameworkVersion>") |
|
line = line[:startpos + 24] + fw + line[endpos:] |
|
|
|
# PlatformToolSet: |
|
# Skip this change when vs=0 |
|
# Search for line with <PlatformToolset> |
|
if vs != 0: |
|
startpos = line.find("<PlatformToolset>") |
|
if startpos != -1: |
|
endpos = line.find("</PlatformToolset>") |
|
line = ( |
|
line[:startpos + 17] |
|
+ platformtoolset[vs] |
|
+ line[endpos:] |
|
) |
|
|
|
# config_tag, to set configuration |
|
startpos = line.find(config_tag) |
|
if startpos != -1: |
|
if line.find(config_val32) != -1: |
|
configuration = 32 |
|
arch = "ia32" |
|
archwin = "ia32_win" |
|
elif line.find(config_val64) != -1: |
|
configuration = 64 |
|
arch = "intel64" |
|
archwin = "intel64_win" |
|
|
|
# IFORT_COMPILER ... |
|
startpos = line.find("$(IFORT_COMPILER") |
|
if startpos != -1: |
|
if oa == -999: |
|
sys.exit( |
|
"ERROR: Fortran compiler specification is being used " |
|
"while not defined.") |
|
split_char = ";" |
|
if line.find("oss-install") != -1: |
|
# |
|
# ... in argument of oss-install |
|
if ptype == "c": |
|
split_char = '"' |
|
parts = line.split(split_char) |
|
i = 0 |
|
lastFound = -1 |
|
it = 0 |
|
for part in parts: |
|
lastFound = part.find( |
|
"$(IFORT_COMPILER", lastFound + 1 |
|
) |
|
if lastFound != -1: |
|
tempStr = "" |
|
while lastFound != -1: |
|
if it == 0: |
|
tempStr += redistdir.format(archwin) |
|
lastFound = part.find( |
|
"$(IFORT_COMPILER", lastFound + 1 |
|
) |
|
elif it == 1: |
|
tempStr += mkldir.format(arch) |
|
lastFound = part.find( |
|
"$(IFORT_COMPILER", lastFound + 1 |
|
) |
|
elif it > 1: |
|
break |
|
it += 1 |
|
parts[i] = tempStr |
|
i += 1 |
|
else: |
|
parts[i] = part |
|
i += 1 |
|
del parts[i:] |
|
line = split_char.join(parts) |
|
if line.find("AdditionalLibraryDirectories") != -1: |
|
# ... in specification of AdditionalLibrarieDirectories |
|
parts = line.split(split_char) |
|
added = False |
|
i = 0 |
|
for part in parts: |
|
startpos = part.find("$(IFORT_COMPILER") |
|
if startpos != -1: |
|
if not added: |
|
parts[i] = parts[i][:startpos] + libdir.format( |
|
arch |
|
) |
|
added = True |
|
i += 1 |
|
# else: |
|
# remove this part |
|
else: |
|
parts[i] = part |
|
i += 1 |
|
del parts[i:] |
|
line = split_char.join(parts) |
|
else: |
|
# Unclear context of using IFORT_COMPILER |
|
# Just replace the version number at the end |
|
startpos = startpos + 16 |
|
endpos = startpos + 2 |
|
line = line[:startpos] + '{:02}'.format(oa % 100) + \ |
|
line[endpos:] |
|
|
|
# UCRTlibdir |
|
# Search string to be replaced: two options: "$(OSS_UCRTLIBDIR)" |
|
# and "$(UniversalCRTSdkDir)lib\..." |
|
# |
|
# $(OSS_UCRTLIBDIR) => $(UniversalCRTSdkDir)lib\... |
|
startpos = line.find("$(OSS_UCRTLIBDIR)") |
|
if startpos != -1: |
|
endpos = startpos + 17 |
|
else: |
|
# $(UniversalCRTSdkDir)lib\... => $(OSS_UCRTLIBDIR) |
|
startpos = line.find("$(UniversalCRTSdkDir)") |
|
if startpos != -1: |
|
quotepos = line[startpos:].find('"') |
|
if quotepos == -1: |
|
quotepos = 999 |
|
colonpos = line[startpos:].find(";") |
|
if colonpos == -1: |
|
colonpos = 999 |
|
endpos = startpos + min(quotepos, colonpos) |
|
# Replace by the correct string. Assumption: |
|
# "UCRTLIBDIRVERSIONNUMBER" is replaced by the correct |
|
# versionnumber when applicable, by executing getUCRTlibdir |
|
if startpos != -1: |
|
line = ( |
|
line[:startpos] + ucrtlibdir[configuration] + line[endpos:] |
|
) |
|
|
|
# Remove PETSC |
|
if remove_petsc: |
|
startpos = line.find("HAVE_PETSC;") |
|
if startpos != -1: |
|
endpos = startpos + 11 |
|
line = line[:startpos] + line[endpos:] |
|
|
|
filouthandle.write(line) |
|
|
|
|
|
def process_cmd_file(cmd): |
|
|
|
if not os.path.isfile(cmd): return |
|
|
|
sys.stdout.write("Updating file " + cmd + " ...\n") |
|
|
|
# Read cmd file: |
|
# Put the full file contents in filin_contents |
|
with open(cmd, "r", encoding="utf-8") as filinhandle: |
|
filin_contents = filinhandle.readlines() |
|
|
|
# Scan the contents and rewrite |
|
with open(cmd, "w", encoding="utf-8") as filouthandle: |
|
|
|
for line in filin_contents: |
|
|
|
if ("!mkl_redist_dir!mkl_" in line and |
|
'.1.dll' not in line): line = line.replace('.dll', '.1.dll') |
|
|
|
filouthandle.write(line) |
|
|
|
sys.stdout.write("...Finished.\n") |
|
sys.stdout.write('Ready to be used: "' + cmd + '"\n') |
|
|
|
|
|
def main(templateSolutionPath=""): |
|
|
|
vs = get_vs_version() |
|
fw = get_net_version() |
|
oa = get_oneAPI_compilers_major_version() |
|
|
|
UCRTlibdir = getUCRTlibdir(vs) |
|
scripts64 = os.path.join("scripts_lgpl", "win64") |
|
|
|
conf_process = lambda fout, fin: process_solution_file(fout, |
|
fin, |
|
vs, |
|
fw, |
|
oa, |
|
UCRTlibdir) |
|
|
|
if not templateSolutionPath: |
|
|
|
conf_process("delft3d_open.sln", |
|
os.path.join("scripts_lgpl", |
|
"win64", |
|
"delft3d_open_template.sln")) |
|
conf_process("dflowfm_open.sln", |
|
os.path.join("engines_gpl", |
|
"dflowfm", |
|
"scripts", |
|
"template", |
|
"dflowfm_open_template.sln")) |
|
conf_process("dimr_open.sln", |
|
os.path.join("engines_gpl", |
|
"dimr", |
|
"scripts", |
|
"template", |
|
"dimr_open_template.sln")) |
|
|
|
# TODO: Consider making this optional via cmdline args: |
|
conf_process("io_netcdf.sln", |
|
os.path.join(scripts64, "io_netcdf_template.sln")) |
|
conf_process("nefis.sln", |
|
os.path.join(scripts64, "nefis_template.sln")) |
|
conf_process("utils_lgpl.sln", |
|
os.path.join(scripts64, "utils_lgpl_template.sln")) |
|
conf_process("tests.sln", |
|
os.path.join(scripts64, "tests_template.sln")) |
|
|
|
else: |
|
|
|
slnName = os.path.basename(templateSolutionPath).replace( |
|
"_template", "") |
|
conf_process(slnName, templateSolutionPath) |
|
|
|
conf_process("ec_module.sln", |
|
os.path.join(scripts64, "ec_module_template.sln")) |
|
process_cmd_file(os.path.join(scripts64, "oss-post_build.cmd")) |
|
process_cmd_file(os.path.join(scripts64, "oss-install_x64.cmd")) |
|
|
|
sys.stdout.write("\n") |
|
sys.stdout.write("Visual Studio Version : " + str(vs) + "\n") |
|
sys.stdout.write(".Net Framework Version : " + str(fw) + "\n") |
|
sys.stdout.write("Intel oneAPI Version : " + str(oa) + "\n") |
|
sys.stdout.write("Solution path : " + templateSolutionPath + "\n") |
|
|
|
return |
|
|
|
if __name__ == "__main__": |
|
main() |
@elappala, better to have the discussion here I think. I was confused because you were posting under the Compiling Deflt3D using Windows Subsystem for Linux README. I'm going to delete the comments there, as they are not relevant.
So, regarding your question:
There should be another solution file named
delft3d_open.sln
in thesrc
directory and this solution should compile the other modules.