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

gn: add initial gn recipe #5433

Merged
merged 12 commits into from
May 10, 2021
3 changes: 3 additions & 0 deletions recipes/gn/all/conandata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sources:
"cci.20210429":
url: "https://gn.googlesource.com/gn/+archive/6771ce569fb4803dad7a427aa2e2c23e960b917e.tar.gz"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no sha256?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It changes all the time.
Some time stamp is probably spoiling the fun.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, their servers produce tarballs on fly, and sha256 is always different, it's pain to package. if someone can help how to get stable tarball URL for gn, help is welcome.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we could mirror this repo on github and have it sync itself automatically using actions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this actions/checkout#24
But it looks like it only works to sync repos on the same domain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's completely possible.
But somebody/organization should invest some time setting it up...

122 changes: 122 additions & 0 deletions recipes/gn/all/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from conans import ConanFile, tools
from conans.errors import ConanInvalidConfiguration
from contextlib import contextmanager
import os
import textwrap
import time

required_conan_version = ">=1.33.0"


class GnConan(ConanFile):
name = "gn"
description = "GN is a meta-build system that generates build files for Ninja."
url = "https://github.com/conan-io/conan-center-index"
topics = ("gn", "build", "system", "ninja")
license = ("LGPL-3.0", "GPL-2.0")
madebr marked this conversation as resolved.
Show resolved Hide resolved
homepage = "https://gn.googlesource.com/"
settings = "os", "arch", "compiler", "build_type"

@property
def _source_subfolder(self):
return "source_subfolder"

@property
def _minimum_compiler_version_supporting_cxx17(self):
return {
"Visual Studio": 15,
"gcc": 7,
"clang": 4,
"apple-clang": 10,
}.get(str(self.settings.compiler))

def configure(self):
if self.settings.compiler.cppstd:
tools.check_min_cppstd(self, 17)
else:
if self._minimum_compiler_version_supporting_cxx17:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen this pattern over and over, I'm wondering if this should be a conan built in tool.
It would remove boilerplate code and eliminate the need from the recipe creator of knowing which c++ std supports each compiler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some util functions would be nice.
But looking at the support matrix at https://en.cppreference.com/w/cpp/compiler_support,
the minimum required compiler version depends on what c++ features a project desires..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. One day... 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C++ standard is not enough since the STL support changes between some minor versions it's a manual process for many projects

if tools.Version(self.settings.compiler.version) < self._minimum_compiler_version_supporting_cxx17:
raise ConanInvalidConfiguration("gn requires a compiler supporting c++17")
else:
self.output.warn("gn recipe does not recognize the compiler. gn requires a compiler supporting c++17. Assuming it does.")

def package_id(self):
del self.info.settings.compiler

def source(self):
tools.get(**self.conan_data["sources"][self.version], destination=self._source_subfolder)

def build_requirements(self):
self.build_requires("ninja/1.10.2")
madebr marked this conversation as resolved.
Show resolved Hide resolved

@contextmanager
def _build_context(self):
if self.settings.compiler == "Visual Studio":
with tools.vcvars(self.settings):
yield
else:
compiler_defaults = {}
if self.settings.compiler == "gcc":
compiler_defaults = {
"CC": "gcc",
"CXX": "g++",
"AR": "ar",
"LD": "g++",
}
elif self.settings.compiler == "clang":
compiler_defaults = {
"CC": "clang",
"CXX": "clang++",
"AR": "ar",
"LD": "clang++",
}
env = {}
for k in ("CC", "CXX", "AR", "LD"):
v = tools.get_env(k, compiler_defaults.get(k, None))
if v:
env[k] = v
with tools.environment_append(env):
yield

@staticmethod
def _to_gn_platform(os_, compiler):
if tools.is_apple_os(os_):
return "darwin"
if compiler == "Visual Studio":
return "msvc"
# Assume gn knows about the os
return str(os_).lower()

def build(self):
with tools.chdir(self._source_subfolder):
with self._build_context():
tools.save(os.path.join("src", "gn", "last_commit_position.h"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment about what this is for?

madebr marked this conversation as resolved.
Show resolved Hide resolved
textwrap.dedent("""\
#pragma once
#define LAST_COMMIT_POSITION "1"
#define LAST_COMMIT_POSITION_NUM 1
"""))
conf_args = [
"--no-last-commit-position",
"--host={}".format(self._to_gn_platform(self.settings.os, self.settings.compiler)),
]
if self.settings.build_type == "Debug":
conf_args.append("-d")
self.run("python build/gen.py {}".format(" ".join(conf_args)), run_environment=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it require cpython in build requirement when packaged? Maybe add a TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll add a todo.

# Try sleeping one second to avoid time skew of the generated ninja.build file (and having to re-run build/gen.py)
time.sleep(1)
build_args = [
"-C", "out",
"-j{}".format(tools.cpu_count()),
]
self.run("ninja {}".format(" ".join(build_args)), run_environment=True)

def package(self):
self.copy("LICENSE", src=self._source_subfolder, dst="licenses")
self.copy("gn", src=os.path.join(self._source_subfolder, "out"), dst="bin")
self.copy("gn.exe", src=os.path.join(self._source_subfolder, "out"), dst="bin")

def package_info(self):
bin_path = os.path.join(self.package_folder, "bin")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these lines really necessary? It is my understanding that conan exposes self.cpp_info.binpath to PATH automatically when a package is listed as a build requirement.

Copy link
Contributor Author

@madebr madebr May 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not needed when doing crossbuilds (-pr:h hostprofile -pr:b buildprofile).
But when using the single profile approach (when your profile contains os_build or arch_build, I think the PATH environment variable is not automatically set.

self.output.info("Appending PATH environment variable: {}".format(bin_path))
self.env_info.PATH.append(bin_path)
2 changes: 2 additions & 0 deletions recipes/gn/all/test_package/.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The location of the build configuration file.
buildconfig = "//config/BUILDCONFIG.gn"
22 changes: 22 additions & 0 deletions recipes/gn/all/test_package/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

executable("test_package") {
sources = ["test_package.cpp"]

deps = [
":test_shared",
":test_static",
]
}

shared_library("test_shared") {
defines = ["TEST_SHARED_BUILD"]
sources = [
"test_shared.cpp",
]
}

static_library("test_static") {
sources = [
"test_static.cpp",
]
}
69 changes: 69 additions & 0 deletions recipes/gn/all/test_package/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from conans import ConanFile, CMake, tools
from contextlib import contextmanager
import os


class TestPackageConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"

def build_requirements(self):
self.build_requires("ninja/1.10.2")

@contextmanager
def _build_context(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this not be re-used some how, I hate duplication like this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These environment variables are closely coupled with the .gn sources in the test package.
This is my first encounter with gn, so I don't really know a better way to let the build system know of another compiler.

if self.settings.compiler == "Visual Studio":
with tools.vcvars(self.settings):
yield
else:
compiler_defaults = {}
if self.settings.compiler == "gcc":
compiler_defaults = {
"CC": "gcc",
"CXX": "g++",
"AR": "ar",
"LD": "g++",
}
elif self.settings.compiler in ("apple-clang", "clang"):
compiler_defaults = {
"CC": "clang",
"CXX": "clang++",
"AR": "ar",
"LD": "clang++",
}
env = {}
for k in ("CC", "CXX", "AR", "LD"):
v = tools.get_env(k, compiler_defaults.get(k, None))
if v:
env[k] = v
with tools.environment_append(env):
yield

@property
def _target_os(self):
if tools.is_apple_os(self.settings.os):
return "mac"
# Assume gn knows about the os
return {
"Windows": "win",
}.get(str(self.settings.os), str(self.settings.os).lower())

@property
def _target_cpu(self):
return {
"x86_64": "x64",
}.get(str(self.settings.arch), str(self.settings.arch))

def build(self):
if not tools.cross_building(self.settings):
with tools.chdir(self.source_folder):
gn_args = [
os.path.relpath(os.path.join(self.build_folder, "bin"), os.getcwd()).replace("\\", "/"),
"--args=\"target_os=\\\"{os_}\\\" target_cpu=\\\"{cpu}\\\"\"".format(os_=self._target_os, cpu=self._target_cpu),
]
self.run("gn gen {}".format(" ".join(gn_args)), run_environment=True)
with self._build_context():
self.run("ninja -v -j{} -C bin".format(tools.cpu_count()), run_environment=True)

def test(self):
if not tools.cross_building(self.settings):
self.run(os.path.join("bin", "test_package"), run_environment=True)
24 changes: 24 additions & 0 deletions recipes/gn/all/test_package/config/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
config("compiler_defaults") {
if (current_os == "linux") {
cflags = [
"-fPIC",
"-pthread",
]
}
if (current_os == "windows") {
cflags_cc = [
"-EHsc",
]
}
}
config("executable_ldconfig") {
if (!is_mac && current_os != "windows") {
ldflags = [
"-Wl,-rpath=\$ORIGIN/",
"-Wl,-rpath-link=",
]
}
}
48 changes: 48 additions & 0 deletions recipes/gn/all/test_package/config/BUILDCONFIG.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

if (target_os == "") {
target_os = host_os
}
if (target_cpu == "") {
target_cpu = host_cpu
}
if (current_cpu == "") {
current_cpu = target_cpu
}
if (current_os == "") {
current_os = target_os
}
is_windows = host_os == "win" && current_os == "win" && target_os == "win"
is_linux = host_os == "linux" && current_os == "linux" && target_os == "linux"
is_mac = host_os == "mac" && current_os == "mac" && target_os == "mac"
# All binary targets will get this list of configs by default.
_shared_binary_target_configs = [ "//config:compiler_defaults" ]
# Apply that default list to the binary target types.
set_defaults("executable") {
configs = _shared_binary_target_configs
# Executables get this additional configuration.
configs += [ "//config:executable_ldconfig" ]
}
set_defaults("static_library") {
configs = _shared_binary_target_configs
}
set_defaults("shared_library") {
configs = _shared_binary_target_configs
}
set_defaults("source_set") {
configs = _shared_binary_target_configs
}
if (is_windows) {
set_default_toolchain("//config/toolchain:msvc")
} else {
set_default_toolchain("//config/toolchain:gcc")
}

print("current_os:", current_os)
print("current_cpu:", current_cpu)
print("host_os:", host_os)
print("host_cpu:", host_cpu)
print("target_os:", target_os)
print("target_cpu:", target_os)
Loading