Skip to content

Premake generator: better integration #17898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: develop2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b1a7ed6
WIP: added premake toolchain and updated premake generator
perseoGI Mar 5, 2025
2ccee8d
Updated premake toolchain
perseoGI Mar 7, 2025
8139a45
Created build method on Premake, fix cross building and map architect…
perseoGI Apr 8, 2025
972671d
Added extra flags on premake generator
perseoGI Apr 10, 2025
bc5a51f
Stable version, not complete
perseoGI Apr 11, 2025
c075cbc
Detailed tooling
perseoGI Apr 11, 2025
845dba0
Improved code generation identation
perseoGI Apr 11, 2025
998d67c
Add option to disable projects
perseoGI Apr 14, 2025
8cc8ecf
Removed global projects as it is not an stable solution
perseoGI Apr 15, 2025
c75839f
Use ConanException instead
perseoGI Apr 15, 2025
cfe86fb
Removed possibility of managing different workspaces
perseoGI Apr 15, 2025
31b6304
Restore multi workspace allowing PremakeToolchain to be used as a pla…
perseoGI Apr 15, 2025
573bf95
Added more premake tests
perseoGI May 12, 2025
794bbff
Parametrize premake test
perseoGI May 13, 2025
baef18e
Added some comments
perseoGI May 13, 2025
de01689
Removed old premake functional test
perseoGI May 13, 2025
088def5
Added docstring and a way of controlling verbosity on premake
perseoGI May 13, 2025
4591f28
Fixed tests
perseoGI May 14, 2025
a23a6ec
Revert extra formatting
perseoGI May 14, 2025
94ebfa0
Remove print
perseoGI May 14, 2025
8ada680
Keep both legacy premake generator logic
perseoGI May 21, 2025
377d1fd
Added new tests for premake generators
perseoGI May 21, 2025
4127b46
Drop support for buildenv flags reading
perseoGI May 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions conan/internal/api/install/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"XcodeDeps": "conan.tools.apple",
"XcodeToolchain": "conan.tools.apple",
"PremakeDeps": "conan.tools.premake",
"PremakeToolchain": "conan.tools.premake",
"MakeDeps": "conan.tools.gnu",
"SConsDeps": "conan.tools.scons",
"QbsDeps": "conan.tools.qbs",
Expand Down
3 changes: 2 additions & 1 deletion conan/tools/premake/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from conan.tools.premake.premake import Premake
from conan.tools.premake.premakedeps import PremakeDeps
from conan.tools.premake.premakedeps import PremakeDeps
from conan.tools.premake.toolchain import PremakeToolchain
32 changes: 32 additions & 0 deletions conan/tools/premake/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Source: https://premake.github.io/docs/architecture/
CONAN_TO_PREMAKE_ARCH = {
"x86": "x86",
"x86_64": "x86_64",

"armv4": "arm",
"armv4i": "arm",
"armv5el": "arm",
"armv5hf": "arm",
"armv6": "arm",
"armv7": "arm",
"armv7hf": "arm",
"armv7s": "arm",
"armv7k": "arm",

"armv8": "arm64",
"armv8_32": "arm64",
"armv8.3": "arm64",
"arm64ec": "arm64",

"e2k-v2": "e2k",
"e2k-v3": "e2k",
"e2k-v4": "e2k",
"e2k-v5": "e2k",
"e2k-v6": "e2k",
"e2k-v7": "e2k",

"riscv64": "riscv64",

"wasm": "wasm32",
"asm.js": "wasm32",
}
100 changes: 89 additions & 11 deletions conan/tools/premake/premake.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
import textwrap
from pathlib import Path

from conan.tools.build.cpu import build_jobs
from jinja2 import Template

from conan.errors import ConanException
from conan.tools.files import save
from conan.tools.microsoft.msbuild import MSBuild
from conan.tools.premake.toolchain import PremakeToolchain
from conan.tools.premake.constants import CONAN_TO_PREMAKE_ARCH

# Source: https://learn.microsoft.com/en-us/cpp/overview/compiler-versions?view=msvc-170
PREMAKE_VS_VERSION = {
'190': '2015',
Expand All @@ -7,34 +19,100 @@
'194': '2022', # still 2022
}


class Premake:
"""
Premake cli wrapper
This class calls Premake commands when a package is being built. Notice that
this one should be used together with the ``PremakeToolchain`` generator.

This premake generator is only compatible with ``premake5``.
"""

filename = "conanfile.premake5.lua"

# Conan premake file which will preconfigure toolchain and then will call the user's premake file
_premake_file_template = textwrap.dedent(
"""\
#!lua
include("{{luafile}}")
include("{{premake_conan_toolchain}}")
"""
)

def __init__(self, conanfile):
"""
:param conanfile: ``< ConanFile object >`` The current recipe object. Always use ``self``.
"""
self._conanfile = conanfile

self.action = None # premake5 action to use (Autogenerated)
self.luafile = 'premake5.lua' # Path to the root (premake5) lua file
# (key value pairs. Will translate to "--{key}={value}")
self.arguments = {} # https://premake.github.io/docs/Command-Line-Arguments/
#: Path to the root premake5 lua file (default is ``premake5.lua``)
self.luafile = (Path(self._conanfile.source_folder) / "premake5.lua").as_posix()
#: Key value pairs. Will translate to "--{key}={value}"
self.arguments = {} # https://premake.github.io/docs/Command-Line-Arguments/

if "msvc" in self._conanfile.settings.compiler:
msvc_version = PREMAKE_VS_VERSION.get(str(self._conanfile.settings.compiler.version))
self.action = f'vs{msvc_version}'
else:
self.action = 'gmake2'
self.action = "gmake" # New generator (old gmakelegacy is deprecated)

self._premake_conan_toolchain = Path(self._conanfile.generators_folder) / PremakeToolchain.filename

@staticmethod
def _expand_args(args):
return ' '.join([f'--{key}={value}' for key, value in args.items()])

def configure(self):
"""
Runs ``premake5 <action> [FILE]`` which will generate respective build scripts depending on the ``action``.
"""
if self._premake_conan_toolchain.exists():
content = Template(self._premake_file_template).render(
premake_conan_toolchain=self._premake_conan_toolchain, luafile=self.luafile
)
conan_luafile = Path(self._conanfile.build_folder) / self.filename
save(self._conanfile, conan_luafile, content)
arch = str(self._conanfile.settings.arch)
if arch not in CONAN_TO_PREMAKE_ARCH:
raise ConanException(f"Premake does not support {arch} architecture.")
self.arguments["arch"] = CONAN_TO_PREMAKE_ARCH[arch]
else:
# Old behavior, for backward compatibility
conan_luafile = self.luafile

premake_options = dict()
premake_options["file"] = self.luafile
premake_options["file"] = f'"{conan_luafile}"'

premake_command = f'premake5 {self._expand_args(premake_options)} {self.action} ' \
f'{self._expand_args(self.arguments)}'
premake_command = (
f"premake5 {self._expand_args(premake_options)} {self.action} "
f"{self._expand_args(self.arguments)}{self._premake_verbosity}"
)
self._conanfile.run(premake_command)

@property
def _premake_verbosity(self):
verbosity = self._conanfile.conf.get("tools.build:verbosity", choices=("quiet", "verbose"))
return " --verbose" if verbosity == "verbose" else ""

@property
def _compilation_verbosity(self):
verbosity = self._conanfile.conf.get("tools.compilation:verbosity", choices=("quiet", "verbose"))
return " --debug" if verbosity == "verbose" else ""

def build(self, workspace, targets=None):
"""
Depending on the action, this method will run either ``msbuild`` or ``make`` with ``N_JOBS``.
You can specify ``N_JOBS`` through the configuration line ``tools.build:jobs=N_JOBS``
in your profile ``[conf]`` section.

:param workspace: ``str`` Specifies the solution to be compiled (only used by ``MSBuild``).
:param targets: ``List[str]`` Declare the projects to be built (None to build all projects).
"""
if not self._premake_conan_toolchain.exists():
raise ConanException(f"Premake.build() method requires PrmakeToolchain to work properly")
if self.action.startswith("vs"):
msbuild = MSBuild(self._conanfile)
msbuild.build(sln=f"{workspace}.sln", targets=targets)
else:
build_type = str(self._conanfile.settings.build_type)
targets = "all" if targets is None else " ".join(targets)
njobs = build_jobs(self._conanfile)
self._conanfile.run(f"make config={build_type.lower()} {targets} -j{njobs}{self._compilation_verbosity}")
17 changes: 7 additions & 10 deletions conan/tools/premake/premakedeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

from conan.internal import check_duplicated_generator
from conan.internal.util.files import save
from conan.tools.premake.constants import CONAN_TO_PREMAKE_ARCH

# Filename format strings
PREMAKE_VAR_FILE = "conan_{pkgname}_vars{config}.premake5.lua"
PREMAKE_VAR_FILE = "conan_{pkgname}_vars_{config}.premake5.lua"
PREMAKE_CONF_FILE = "conan_{pkgname}{config}.premake5.lua"
PREMAKE_PKG_FILE = "conan_{pkgname}.premake5.lua"
PREMAKE_ROOT_FILE = "conandeps.premake5.lua"
Expand Down Expand Up @@ -145,10 +146,7 @@ def generate(self):
save(generator_file, content)

def _config_suffix(self):
props = [("Configuration", self.configuration),
("Platform", self.architecture)]
name = "".join("_%s" % v for _, v in props)
return name.lower()
return f"{self.configuration}_{CONAN_TO_PREMAKE_ARCH[str(self.architecture)]}".lower()

def _output_lua_file(self, filename, content):
self.output_files[filename] = "\n".join(["#!lua", *content])
Expand Down Expand Up @@ -180,8 +178,7 @@ def content(self):
check_duplicated_generator(self, self._conanfile)

self.output_files = {}
conf_suffix = str(self._config_suffix())
conf_name = conf_suffix[1::]
conf_name = self._config_suffix()

# Global utility file
self._output_lua_file("conanutils.premake5.lua", [PREMAKE_TEMPLATE_UTILS])
Expand All @@ -206,15 +203,15 @@ def content(self):
dep_aggregate = dep.cpp_info.aggregated_components()

# Generate config dependent package variable and setup premake file
var_filename = PREMAKE_VAR_FILE.format(pkgname=dep_name, config=conf_suffix)
var_filename = PREMAKE_VAR_FILE.format(pkgname=dep_name, config=conf_name)
self._output_lua_file(var_filename, [
PREMAKE_TEMPLATE_VAR.format(pkgname=dep_name,
config=conf_name, deps=_PremakeTemplate(dep_aggregate))
])

# Create list of all available profiles by searching on disk
file_pattern = PREMAKE_VAR_FILE.format(pkgname=dep_name, config="_*")
file_regex = PREMAKE_VAR_FILE.format(pkgname=re.escape(dep_name), config="_(([^_]*)_(.*))")
file_pattern = PREMAKE_VAR_FILE.format(pkgname=dep_name, config="*")
file_regex = PREMAKE_VAR_FILE.format(pkgname=re.escape(dep_name), config="(([^_]*)_(.*))")
available_files = glob.glob(file_pattern)
# Add filename of current generations var file if not already present
if var_filename not in available_files:
Expand Down
Loading
Loading