Skip to content
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

Premake generator: better integration #17898

Draft
wants to merge 2 commits into
base: develop2
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
80 changes: 64 additions & 16 deletions conan/tools/premake/premake.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import textwrap
from pathlib import Path

from jinja2 import Template

from conan.tools.files import save
from conan.tools.microsoft.msbuild import MSBuild
from conan.tools.premake.toolchain import PremakeToolchain

# Source: https://learn.microsoft.com/en-us/cpp/overview/compiler-versions?view=msvc-170
PREMAKE_VS_VERSION = {
'190': '2015',
'191': '2017',
'192': '2019',
'193': '2022',
'194': '2022', # still 2022
"190": "2015",
"191": "2017",
"192": "2019",
"193": "2022",
"194": "2022", # still 2022
}


Expand All @@ -13,28 +22,67 @@ class Premake:
Premake cli wrapper
"""

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}}")
{% if has_conan_toolchain %}
include("conantoolchain.premake5.lua")
{% endif %}
"""
)

def __init__(self, conanfile):
self._conanfile = conanfile

self.action = None # premake5 action to use (Autogenerated)
self.luafile = 'premake5.lua' # Path to the root (premake5) lua file
# Path to the root (premake5) lua file
self.luafile = Path(self._conanfile.source_folder) / "premake5.lua"
# (key value pairs. Will translate to "--{key}={value}")
self.arguments = {} # https://premake.github.io/docs/Command-Line-Arguments/
self.arguments = {} # https://premake.github.io/docs/Command-Line-Arguments/
self.arguments["scripts"] = self._conanfile.generators_folder
if self._conanfile.settings.get_safe("arch"):
self.arguments["arch"] = self._conanfile.settings.arch

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}'
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"

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

def configure(self):
has_conan_toolchain = (
Path(self._conanfile.generators_folder) / PremakeToolchain.filename
).exists()
content = Template(self._premake_file_template).render(
has_conan_toolchain=has_conan_toolchain, luafile=self.luafile
)

conan_luafile = Path(self._conanfile.build_folder) / self.filename
save(self._conanfile, conan_luafile, content)

premake_options = dict()
premake_options["file"] = self.luafile
premake_options["file"] = 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._conanfile.run(premake_command)


def build(self):
if self.action.startswith("vs"):
msbuild = MSBuild(self)
# TODO determine the generated solution name from premake build script
# msbuild.build(sln=".sln")
else:
build_type = str(self._conanfile.settings.build_type)
self._conanfile.run(f"make config={build_type.lower()} -j")
75 changes: 75 additions & 0 deletions conan/tools/premake/toolchain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import os
import textwrap

from jinja2 import Template

from conan.tools.files import save


class PremakeToolchain:
"""
PremakeToolchain generator
"""

filename = "conantoolchain.premake5.lua"

_premake_file_template = textwrap.dedent(
"""\
#!lua
include("conandeps.premake5.lua")


workspace "{{workspace}}"
premake.api.addAliases("architecture", {
["armv8"] = "arm64"
})

{% if cppstd %}
cppdialect "{{cppstd}}"
{% endif %}
{% if cstd %}
cdialect "{{cstd}}"
{% endif %}
location "{{ build_folder }}"
targetdir "{{ build_folder }}"
conan_setup()

{% if variables %}
defines { {{variables}} }
{% endif %}
"""
)

def __init__(self, conanfile, workspace="*"):
# '*' is the global workspace
self._conanfile = conanfile
self.workspace = workspace
# TODO: not possible to overwrite upstream defines yet
self.defines = {}

def generate(self):
cppstd = self._conanfile.settings.get_safe("compiler.cppstd")
cstd = self._conanfile.settings.get_safe("compiler.cstd")
if cppstd.startswith("gnu"):
cppstd = f"gnu++{cppstd[3:]}"

formated_variables = ""
for key, value in self.defines.items():
if isinstance(value, bool):
value = 1 if value else 0
formated_variables += f'"{key}={value}", '

content = Template(
self._premake_file_template, trim_blocks=True, lstrip_blocks=True
).render(
workspace=self.workspace,
build_folder=self._conanfile.build_folder,
cppstd=cppstd,
cstd=cstd,
variables=formated_variables,
)
save(
self,
os.path.join(self._conanfile.generators_folder, self.filename),
content,
)
22 changes: 22 additions & 0 deletions test/integration/toolchains/premake/test_premake.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,25 @@ def build(self):
c.save({"conanfile.py": conanfile})
c.run("build . -s compiler=msvc -s compiler.version=193 -s compiler.runtime=dynamic")
assert "conanfile.py: Running premake5 --file=myproject.lua vs2022 --myarg=myvalue!!" in c.out



def test_premake_compile():
c = TestClient()
conanfile = textwrap.dedent("""
from conan import ConanFile
from conan.tools.premake import Premake

class Pkg(ConanFile):
settings = "os", "compiler", "build_type", "arch"
def run(self, cmd, *args, **kwargs):
self.output.info(f"Running {cmd}!!")
def build(self):
premake = Premake(self)
premake.luafile = "myproject.lua"
premake.arguments = {"myarg": "myvalue"}
premake.configure()
""")
c.save({"conanfile.py": conanfile})
c.run("build . -s compiler=msvc -s compiler.version=193 -s compiler.runtime=dynamic")
assert "conanfile.py: Running premake5 --file=myproject.lua vs2022 --myarg=myvalue!!" in c.out
114 changes: 114 additions & 0 deletions test/integration/toolchains/premake/test_premakedeps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import textwrap

from conan.test.utils.mocks import ConanFileMock
from conan.test.utils.tools import TestClient
from conan.tools.env.environment import environment_wrap_command
from conan.test.assets.genconanfile import GenConanfile


def assert_vars_file(client, configuration):
Expand Down Expand Up @@ -62,3 +65,114 @@ def package_info(self):
# Assert package per configuration files
assert_vars_file(client, 'debug')
assert_vars_file(client, 'release')


def test_todo():
# Create package
client = TestClient()
# client.run("remote add conancenter https://center2.conan.io")

def run_pkg(msg):
host_arch = client.get_default_host_profile().settings['arch']
cmd_release = environment_wrap_command(ConanFileMock(), f"conanrunenv-release-{host_arch}",
client.current_folder, "dep")
client.run_command(cmd_release)
assert "{}: Hello World Release!".format(msg) in client.out

client.run("new cmake_lib -d name=dep -d version=1.0 -o dep")

consumer_source = textwrap.dedent("""
#include <iostream>
#include "dep.h"

int main(void) {
dep();
std::cout << "Hello World" << std::endl;
return 0;
}
""")

premake5 = textwrap.dedent("""
include("conandeps.premake5.lua")

workspace "HelloWorld"
configurations { "Debug", "Release" }

project "HelloWorld"
kind "ConsoleApp"
language "C++"
targetdir "bin/%{cfg.buildcfg}"

files { "**.h", "**.cpp" }
conan_setup()

filter "configurations:Debug"
defines { "DEBUG" }
symbols "On"

filter "configurations:Release"
defines { "NDEBUG" }
optimize "On"
""")


conanfile = textwrap.dedent("""
from conan import ConanFile
from conan import ConanFile
from conan.tools.files import copy, get, collect_libs, chdir, save, replace_in_file
from conan.tools.layout import basic_layout
from conan.tools.microsoft import MSBuild
from conan.tools.premake import Premake, PremakeDeps
import os

class Pkg(ConanFile):
settings = "os", "compiler", "build_type", "arch"
name = "pkg"
version = "1.0"
exports_sources = '*'

def layout(self):
basic_layout(self, src_folder="src")

def requirements(self):
self.requires("dep/1.0")

def generate(self):
deps = PremakeDeps(self)
deps.generate()

def build(self):
with chdir(self, self.source_folder):
premake = Premake(self)
premake.arguments = {"scripts": "../build-release/conan"}
premake.configure()
if self.settings.os == "Windows":
pass
# msbuild = MSBuild(self)
# msbuild.build("Yojimbo.sln")
else:
build_type = str(self.settings.build_type)
self.run(f"make config={build_type.lower()} -j")

def package(self):
copy(self, "*.h", os.path.join(self.source_folder, "include"), os.path.join(self.package_folder, "include", "pkg"))
for lib in ("*.lib", "*.a"):
copy(self, lib, self.source_folder, os.path.join(self.package_folder, "lib"), keep_path=False)
""")

client.save({"consumer/conanfile.py": conanfile,
"consumer/src/hello.cpp": consumer_source,
"consumer/src/premake5.lua": premake5,
})

client.run("create dep")
client.run("create consumer --build=missing")
build_folder = client.created_layout().build()
print(build_folder)

print(client.out)
client.run("install consumer")
run_pkg("Hello World")

print(client.out)

Loading