Files
conan-center-index/recipes/emsdk/all/conanfile.py
2023-04-14 10:44:55 +02:00

197 lines
8.9 KiB
Python

from conan import ConanFile, conan_version
from conan.tools.build import cross_building
from conan.tools.env import Environment
from conan.tools.files import chdir, copy, get, replace_in_file
from conan.tools.layout import basic_layout
from conan.tools.scm import Version
import json
import os
required_conan_version = ">=1.52.0"
class EmSDKConan(ConanFile):
name = "emsdk"
description = "Emscripten SDK. Emscripten is an Open Source LLVM to JavaScript compiler"
url = "https://github.com/conan-io/conan-center-index"
homepage = "https://github.com/kripken/emscripten"
topics = ("emsdk", "emscripten", "sdk")
license = "MIT"
settings = "os", "arch", "compiler", "build_type"
short_paths = True
@property
def _settings_build(self):
return getattr(self, "settings_build", self.settings)
def layout(self):
basic_layout(self, src_folder="src")
def requirements(self):
self.requires("nodejs/16.3.0")
# self.requires("python") # FIXME: Not available as Conan package
# self.requires("wasm") # FIXME: Not available as Conan package
def package_id(self):
del self.info.settings.compiler
del self.info.settings.build_type
def source(self):
get(self, **self.conan_data["sources"][self.version],
destination=self.source_folder, strip_root=True)
@property
def _relative_paths(self):
return ["bin", os.path.join("bin", "upstream", "emscripten")]
@property
def _paths(self):
return [os.path.join(self.package_folder, path) for path in self._relative_paths]
@property
def _emsdk(self):
return os.path.join(self.package_folder, "bin")
@property
def _emscripten(self):
return os.path.join(self.package_folder, "bin", "upstream", "emscripten")
@property
def _em_config(self):
return os.path.join(self.package_folder, "bin", ".emscripten")
@property
def _em_cache(self):
return os.path.join(self.package_folder, "bin", ".emscripten_cache")
def generate(self):
env = Environment()
env.prepend_path("PATH", self._paths)
env.define_path("EMSDK", self._emsdk)
env.define_path("EMSCRIPTEN", self._emscripten)
env.define_path("EM_CONFIG", self._em_config)
env.define_path("EM_CACHE", self._em_cache)
env.vars(self, scope="emsdk").save_script("emsdk_env_file")
@staticmethod
def _chmod_plus_x(filename):
if os.name == "posix":
os.chmod(filename, os.stat(filename).st_mode | 0o111)
def _tools_for_version(self):
ret = {}
# Select release-upstream from version (wasm-binaries)
with open(os.path.join(self.source_folder, "emscripten-releases-tags.json"), "r") as f:
data = json.load(f)
ret["wasm"] = f"releases-upstream-{data['releases'][self.version]}-64bit"
# Select python and node versions
with open(os.path.join(self.source_folder, "emsdk_manifest.json"), "r") as f:
data = json.load(f)
tools = data["tools"]
if self.settings.os == "Windows":
python = next((it for it in tools if (it["id"] == "python" and not it.get("is_old", False))), None)
ret["python"] = f"python-{python['version']}-64bit"
node = next((it for it in tools if (it["id"] == "node" and not it.get("is_old", False))), None)
ret["nodejs"] = f"node-{node['version']}-64bit"
return ret
def build(self):
with chdir(self, self.source_folder):
emsdk = "emsdk.bat" if self._settings_build.os == "Windows" else "./emsdk"
self._chmod_plus_x("emsdk")
# Install required tools
required_tools = self._tools_for_version()
for key, value in required_tools.items():
if key != 'nodejs':
self.run(f"{emsdk} install {value}")
self.run(f"{emsdk} activate {value}")
def package(self):
copy(self, "LICENSE", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))
copy(self, "*", src=self.source_folder, dst=os.path.join(self.package_folder, "bin"))
emscripten = os.path.join(self.package_folder, "bin", "upstream", "emscripten")
toolchain = os.path.join(emscripten, "cmake", "Modules", "Platform", "Emscripten.cmake")
# FIXME: conan should add the root of conan package requirements to CMAKE_PREFIX_PATH (LIBRARY/INCLUDE -> ONLY; PROGRAM -> NEVER)
# allow to find conan libraries
replace_in_file(self, toolchain,
"set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)",
"set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)")
replace_in_file(self, toolchain,
"set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)",
"set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)")
replace_in_file(self, toolchain,
"set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)",
"set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)")
if not cross_building(self):
self.run("embuilder build MINIMAL", env=["conanemsdk", "conanrun"]) # force cache population
# the line below forces emscripten to accept the cache as-is, even after re-location
# https://github.com/emscripten-core/emscripten/issues/15053#issuecomment-920950710
os.remove(os.path.join(self._em_cache, "sanity.txt"))
def _define_tool_var(self, value):
suffix = ".bat" if self.settings.os == "Windows" else ""
path = os.path.join(self._emscripten, f"{value}{suffix}")
self._chmod_plus_x(path)
return path
def package_info(self):
self.cpp_info.bindirs = self._relative_paths
self.cpp_info.includedirs = []
self.cpp_info.libdirs = []
self.cpp_info.resdirs = []
# If we are not building for Emscripten, probably we don't want to inject following environment variables,
# but it might be legit use cases... until we find them, let's be conservative.
if not hasattr(self, "settings_target") or self.settings_target is None:
return
if self.settings_target.os != "Emscripten":
self.output.warning(f"You've added {self.name}/{self.version} as a build requirement, while os={self.settings_target.os} != Emscripten")
return
toolchain = os.path.join(self.package_folder, "bin", "upstream", "emscripten", "cmake", "Modules", "Platform", "Emscripten.cmake")
self.conf_info.prepend("tools.cmake.cmaketoolchain:user_toolchain", toolchain)
self.buildenv_info.define_path("EMSDK", self._emsdk)
self.buildenv_info.define_path("EMSCRIPTEN", self._emscripten)
self.buildenv_info.define_path("EM_CONFIG", self._em_config)
self.buildenv_info.define_path("EM_CACHE", self._em_cache)
compiler_executables = {
"c": self._define_tool_var("emcc"),
"cpp": self._define_tool_var("em++"),
}
self.conf_info.update("tools.build:compiler_executables", compiler_executables)
self.buildenv_info.define_path("CC", compiler_executables["c"])
self.buildenv_info.define_path("CXX", compiler_executables["cpp"])
self.buildenv_info.define_path("AR", self._define_tool_var("emar"))
self.buildenv_info.define_path("NM", self._define_tool_var("emnm"))
self.buildenv_info.define_path("RANLIB", self._define_tool_var("emranlib"))
self.buildenv_info.define_path("STRIP", self._define_tool_var("emstrip"))
self.cpp_info.builddirs = [
os.path.join("bin", "releases", "src"),
os.path.join("bin", "upstream", "emscripten", "cmake", "Modules"),
os.path.join("bin", "upstream", "emscripten", "cmake", "Modules", "Platform"),
os.path.join("bin", "upstream", "emscripten", "system", "lib", "libunwind", "cmake", "Modules"),
os.path.join("bin", "upstream", "emscripten", "system", "lib", "libunwind", "cmake"),
os.path.join("bin", "upstream", "emscripten", "tests", "cmake", "target_library"),
os.path.join("bin", "upstream", "lib", "cmake", "llvm"),
]
if Version(conan_version).major < 2:
self.env_info.PATH.extend(self._paths)
self.env_info.CONAN_CMAKE_TOOLCHAIN_FILE = toolchain
self.env_info.EMSDK = self._emsdk
self.env_info.EMSCRIPTEN = self._emscripten
self.env_info.EM_CONFIG = self._em_config
self.env_info.EM_CACHE = self._em_cache
self.env_info.CC = compiler_executables["c"]
self.env_info.CXX = compiler_executables["cpp"]
self.env_info.AR = self._define_tool_var("emar")
self.env_info.NM = self._define_tool_var("emnm")
self.env_info.RANLIB = self._define_tool_var("emranlib")
self.env_info.STRIP = self._define_tool_var("emstrip")