🔧 Added support for building socketw with conan v2

This commit is contained in:
Edgar 2022-10-15 13:07:30 +02:00
parent ab4b1a8202
commit 87a934a722
No known key found for this signature in database
GPG Key ID: 17D930BB616061A5
16 changed files with 506 additions and 6 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ tmp/
upload.*
.idea/
build_tmp.py
__pycache__

0
linter/__init__.py Normal file
View File

View File

@ -0,0 +1,28 @@
from pylint.checkers import BaseChecker
from pylint.interfaces import IAstroidChecker
from astroid import nodes, Const, AssignName
class ImportConanFile(BaseChecker):
"""
Import ConanFile from new 'conan' module
"""
__implements__ = IAstroidChecker
name = "conan-import-conanfile"
msgs = {
"E9006": (
"Import ConanFile from new module: `from conan import ConanFile`. Old import is deprecated in Conan v2.",
"conan-import-conanfile",
"Import ConanFile from new module: `from conan import ConanFile`. Old import is deprecated in Conan v2.",
),
}
def visit_importfrom(self, node: nodes.ImportFrom) -> None:
basename = node.modname
if basename == 'conans':
names = [name for name, _ in node.names]
if 'ConanFile' in names:
self.add_message("conan-import-conanfile", node=node)

View File

@ -0,0 +1,77 @@
from pylint.checkers import BaseChecker
from pylint.interfaces import IAstroidChecker
from astroid import nodes, Const, AssignName
class ImportErrors(BaseChecker):
"""
Import errors from new 'conan' module
"""
__implements__ = IAstroidChecker
name = "conan-import-errors"
msgs = {
"E9008": (
"Import errors from new module: `from conan import errors`. Old import is deprecated in Conan v2.",
"conan-import-errors",
"Import errors from new module: `from conan import errors`. Old import is deprecated in Conan v2.",
),
}
def visit_importfrom(self, node: nodes.ImportFrom) -> None:
basename = node.modname
if basename == 'conans':
names = [name for name, _ in node.names]
if 'errors' in names:
self.add_message("conan-import-errors", node=node)
class ImportErrorsConanException(BaseChecker):
"""
Import errors from new 'conan' module
"""
__implements__ = IAstroidChecker
name = "conan-import-error-conanexception"
msgs = {
"E9009": (
"Import ConanException from new module: `from conan.errors import ConanException`. Old import is deprecated in Conan v2.",
"conan-import-error-conanexception",
"Import ConanException from new module: `from conan.errors import ConanException`. Old import is deprecated in Conan v2.",
),
}
def visit_importfrom(self, node: nodes.ImportFrom) -> None:
basename = node.modname
if basename == 'conans.errors':
names = [name for name, _ in node.names]
if 'ConanException' in names:
self.add_message("conan-import-error-conanexception", node=node)
class ImportErrorsConanInvalidConfiguration(BaseChecker):
"""
Import errors from new 'conan' module
"""
__implements__ = IAstroidChecker
name = "conan-import-error-conaninvalidconfiguration"
msgs = {
"E9010": (
"Import ConanInvalidConfiguration from new module: `from conan.errors import ConanInvalidConfiguration`. Old import is deprecated in Conan v2.",
"conan-import-error-conaninvalidconfiguration",
"Import ConanInvalidConfiguration from new module: `from conan.errors import ConanInvalidConfiguration`. Old import is deprecated in Conan v2.",
),
}
def visit_importfrom(self, node: nodes.ImportFrom) -> None:
basename = node.modname
if basename == 'conans.errors':
names = [name for name, _ in node.names]
if 'ConanInvalidConfiguration' in names:
self.add_message("conan-import-error-conaninvalidconfiguration", node=node)

View File

@ -0,0 +1,30 @@
import re
from email.mime import base
from pylint.checkers import BaseChecker
from pylint.interfaces import IAstroidChecker
from astroid import nodes, Const, AssignName
class ImportTools(BaseChecker):
"""
Import tools following pattern 'from conan.tools.xxxx import yyyyy'
"""
__implements__ = IAstroidChecker
name = "conan-import-tools"
msgs = {
"E9011": (
"Import tools following pattern 'from conan.tools.xxxx import yyyyy' (https://docs.conan.io/en/latest/reference/conanfile/tools.html).",
"conan-import-tools",
"Import tools following pattern 'from conan.tools.xxxx import yyyyy' (https://docs.conan.io/en/latest/reference/conanfile/tools.html).",
),
}
def visit_importfrom(self, node: nodes.ImportFrom) -> None:
basename = node.modname
names = [name for name, _ in node.names]
if basename == 'conan' and 'tools' in names:
self.add_message("conan-import-tools", node=node)
elif re.match(r'conan\.tools\.[^.]+\..+', basename):
self.add_message("conan-import-tools", node=node)

View File

@ -0,0 +1,30 @@
from pylint.checkers import BaseChecker
from pylint.interfaces import IAstroidChecker
from astroid import nodes, Const, AssignName
class NoPackageName(BaseChecker):
"""
Conanfile used for testing a package should NOT provide a name
"""
__implements__ = IAstroidChecker
name = "conan-test-package-name"
msgs = {
"E9007": (
"No 'name' attribute in test_package conanfile",
"conan-test-no-name",
"No 'name' attribute in test_package conanfile."
)
}
def visit_classdef(self, node: nodes) -> None:
if node.basenames == ['ConanFile']:
for attr in node.body:
children = list(attr.get_children())
if len(children) == 2 and \
isinstance(children[0], AssignName) and \
children[0].name == "name" and \
isinstance(children[1], Const):
self.add_message("conan-test-no-name", node=attr, line=attr.lineno)

View File

@ -0,0 +1,39 @@
from pylint.checkers import BaseChecker
from pylint.interfaces import IAstroidChecker
from astroid import nodes, Const, AssignName
class PackageName(BaseChecker):
"""
All packages must have a lower-case name
"""
__implements__ = IAstroidChecker
name = "conan-package-name"
msgs = {
"E9004": (
"Reference name should be all lowercase",
"conan-bad-name",
"Use only lower-case on the package name: `name = 'foobar'`."
),
"E9005": (
"Missing name attribute",
"conan-missing-name",
"The member attribute `name` must be declared: `name = 'foobar'`."
)
}
def visit_classdef(self, node: nodes) -> None:
if node.basenames == ['ConanFile']:
for attr in node.body:
children = list(attr.get_children())
if len(children) == 2 and \
isinstance(children[0], AssignName) and \
children[0].name == "name" and \
isinstance(children[1], Const):
value = children[1].as_string()
if value.lower() != value:
self.add_message("conan-bad-name", node=attr, line=attr.lineno)
return
self.add_message("conan-missing-name", node=node)

View File

@ -0,0 +1,20 @@
"""
Pylint plugin/rules for test_package folder in Conan Center Index
"""
from pylint.lint import PyLinter
from linter.check_import_conanfile import ImportConanFile
from linter.check_no_test_package_name import NoPackageName
from linter.check_import_errors import ImportErrorsConanException, ImportErrorsConanInvalidConfiguration, ImportErrors
from linter.check_import_tools import ImportTools
def register(linter: PyLinter) -> None:
linter.register_checker(NoPackageName(linter))
linter.register_checker(ImportConanFile(linter))
linter.register_checker(ImportErrors(linter))
linter.register_checker(ImportErrorsConanException(linter))
linter.register_checker(ImportErrorsConanInvalidConfiguration(linter))
linter.register_checker(ImportTools(linter))

View File

@ -0,0 +1,20 @@
"""
Pylint plugin/rules for conanfiles in Conan Center Index
"""
from pylint.lint import PyLinter
from linter.check_package_name import PackageName
from linter.check_import_conanfile import ImportConanFile
from linter.check_import_errors import ImportErrorsConanException, ImportErrorsConanInvalidConfiguration, ImportErrors
from linter.check_import_tools import ImportTools
def register(linter: PyLinter) -> None:
linter.register_checker(PackageName(linter))
linter.register_checker(ImportConanFile(linter))
linter.register_checker(ImportErrors(linter))
linter.register_checker(ImportErrorsConanException(linter))
linter.register_checker(ImportErrorsConanInvalidConfiguration(linter))
linter.register_checker(ImportTools(linter))

30
linter/pylintrc_recipe Normal file
View File

@ -0,0 +1,30 @@
[MASTER]
load-plugins=linter.conanv2_transition,
linter.transform_conanfile,
linter.transform_imports
py-version=3.6
recursive=no
suggestion-mode=yes
unsafe-load-any-extension=no
[MESSAGES CONTROL]
disable=fixme,
line-too-long,
missing-module-docstring,
missing-function-docstring,
missing-class-docstring,
invalid-name,
wrong-import-order, # TODO: Remove
import-outside-toplevel # TODO: Remove
enable=conan-bad-name,
conan-missing-name,
conan-import-conanfile
[REPORTS]
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error) / statement) * 10))
output-format=text
reports=no
score=no

View File

@ -0,0 +1,27 @@
[MASTER]
load-plugins=linter.conanv2_test_transition,
linter.transform_conanfile,
linter.transform_imports
py-version=3.6
recursive=no
suggestion-mode=yes
unsafe-load-any-extension=no
[MESSAGES CONTROL]
disable=fixme,
line-too-long,
missing-module-docstring,
missing-function-docstring,
missing-class-docstring,
invalid-name,
wrong-import-order, # TODO: Remove
import-outside-toplevel # TODO: Remove
enable=conan-test-no-name,
conan-import-conanfile
[REPORTS]
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error) / statement) * 10))
output-format=text
reports=no
score=no

43
linter/recipe_linter.json Normal file
View File

@ -0,0 +1,43 @@
{
"problemMatcher": [
{
"owner": "recipe_linter_fatals",
"severity": "error",
"pattern": [
{
"regexp": "(\\S+):(\\d+): \\[(F\\d+\\(\\S+\\)),\\s(.+?)?\\](.+)",
"file": 1,
"line": 2,
"message": 5,
"code": 3
}
]
},
{
"owner": "recipe_linter_errors",
"severity": "error",
"pattern": [
{
"regexp": "(\\S+):(\\d+): \\[(E\\d+\\(\\S+\\)),\\s(.+?)?\\](.+)",
"file": 1,
"line": 2,
"message": 5,
"code": 3
}
]
},
{
"owner": "recipe_linter_warnings",
"severity": "warning",
"pattern": [
{
"regexp": "(\\S+):(\\d+): \\[(W\\d+\\(\\S+\\)),\\s(.+?)?\\](.+)",
"file": 1,
"line": 2,
"message": 5,
"code": 3
}
]
}
]
}

View File

@ -0,0 +1,74 @@
# Class ConanFile doesn't declare all the valid members and functions,
# some are injected by Conan dynamically to the class.
import textwrap
import astroid
from astroid.builder import AstroidBuilder
from astroid.manager import AstroidManager
def _settings_transform():
module = AstroidBuilder(AstroidManager()).string_build(
textwrap.dedent("""
class Settings(object):
os = None
arch = None
compiler = None
build_type = None
""")
)
return module['Settings']
def _user_info_build_transform():
module = AstroidBuilder(AstroidManager()).string_build(
textwrap.dedent("""
class UserInfoBuild(defaultdict):
pass
""")
)
return module['UserInfoBuild']
def register(_):
pass
def transform_conanfile(node):
"""Transform definition of ConanFile class so dynamic fields are visible to pylint"""
str_class = astroid.builtin_lookup("str")
dict_class = astroid.builtin_lookup("dict")
info_class = astroid.MANAGER.ast_from_module_name("conans.model.info").lookup(
"ConanInfo")
build_requires_class = astroid.MANAGER.ast_from_module_name(
"conans.client.graph.graph_manager").lookup("_RecipeBuildRequires")
file_copier_class = astroid.MANAGER.ast_from_module_name(
"conans.client.file_copier").lookup("FileCopier")
file_importer_class = astroid.MANAGER.ast_from_module_name(
"conans.client.importer").lookup("_FileImporter")
python_requires_class = astroid.MANAGER.ast_from_module_name(
"conans.client.graph.python_requires").lookup("PyRequires")
dynamic_fields = {
"conan_data": str_class,
"build_requires": build_requires_class,
"tool_requires": build_requires_class,
"info_build": info_class,
"user_info_build": [_user_info_build_transform()],
"info": info_class,
"copy": file_copier_class,
"copy_deps": file_importer_class,
"python_requires": [str_class, python_requires_class],
"recipe_folder": str_class,
"settings_build": [_settings_transform()],
"settings_target": [_settings_transform()],
"conf": dict_class,
}
for f, t in dynamic_fields.items():
node.locals[f] = [i for i in t]
astroid.MANAGER.register_transform(
astroid.ClassDef, transform_conanfile,
lambda node: node.qname() == "conans.model.conan_file.ConanFile")

View File

@ -0,0 +1,46 @@
import astroid
from pylint.lint import PyLinter
"""
Here we are transforming the imports to mimic future Conan v2 release. With
these changes, built-in checks in Pylint will raise with different errors, so
we are modifying the messages to point users in the right direction.
"""
def register(linter: PyLinter):
msge1101 = linter.msgs_store._messages_definitions["E1101"]
msge1101.msg += ". Please, check https://github.com/conan-io/conan-center-index/blob/master/docs/v2_linter.md"
linter.msgs_store.register_message(msge1101)
msge0611 = linter.msgs_store._messages_definitions["E0611"]
msge0611.msg += ". Please, check https://github.com/conan-io/conan-center-index/blob/master/docs/v2_linter.md"
linter.msgs_store.register_message(msge0611)
def transform_tools(module):
""" Transform import module """
if 'get' in module.locals:
del module.locals['get']
if 'cross_building' in module.locals:
del module.locals['cross_building']
if 'rmdir' in module.locals:
del module.locals['rmdir']
if 'Version' in module.locals:
del module.locals['Version']
def transform_errors(module):
pass
#if 'ConanInvalidConfiguration' in module.locals:
# del module.locals['ConanInvalidConfiguration']
#if 'ConanException' in module.locals:
# del module.locals['ConanException']
astroid.MANAGER.register_transform(
astroid.Module, transform_tools,
lambda node: node.qname() == "conans.tools")
astroid.MANAGER.register_transform(
astroid.Module, transform_errors,
lambda node: node.qname() == "conans.errors")

26
linter/yamllint_rules.yml Normal file
View File

@ -0,0 +1,26 @@
extends: default
rules:
document-start:
level: error
present: false
document-end:
level: error
present: false
empty-values:
forbid-in-block-mappings: true
forbid-in-flow-mappings: true
line-length: disable
indentation:
level: error
new-line-at-end-of-file:
level: error
trailing-spaces:
level: error
comments:
level: error
comments-indentation:
level: error
new-lines:
type: unix
key-duplicates:
level: error

View File

@ -1,5 +1,6 @@
from conans import ConanFile, CMake, tools
from conan import ConanFile
from conan.tools.files import get, collect_libs
from conan.tools.cmake import CMakeToolchain, CMake, CMakeDeps, cmake_layout
class SocketwConan(ConanFile):
name = "socketw"
@ -7,14 +8,22 @@ class SocketwConan(ConanFile):
url = "https://github.com/RigsOfRods/socketw/issues"
description = "SocketW is a library which provides cross-platform socket abstraction"
settings = "os", "compiler", "build_type", "arch"
generators = "cmake"
def layout(self):
cmake_layout(self)
def requirements(self):
for req in self.conan_data["requirements"]:
self.requires(req)
def source(self):
tools.get(**self.conan_data["sources"][self.version], strip_root=True)
get(self, **self.conan_data["sources"][self.version], strip_root=True)
def generate(self):
tc = CMakeToolchain(self)
tc.generate()
deps = CMakeDeps(self)
deps.generate()
def build(self):
cmake = CMake(self)
@ -26,4 +35,4 @@ class SocketwConan(ConanFile):
cmake.install()
def package_info(self):
self.cpp_info.libs = tools.collect_libs(self)
self.cpp_info.libs = collect_libs(self)