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

Flex: Conan v2 support #14013

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 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
46 changes: 24 additions & 22 deletions recipes/flex/all/conanfile.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from conans import ConanFile, AutoToolsBuildEnvironment, tools
from conans.errors import ConanInvalidConfiguration
import functools
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.build import cross_building
from conan.tools.files import get, rmdir, rm
from conan.tools.gnu import Autotools, AutotoolsToolchain, AutotoolsDeps
from conan.tools.env import VirtualBuildEnv
import os

required_conan_version = ">=1.33.0"
Expand All @@ -24,20 +27,16 @@ class FlexConan(ConanFile):
"fPIC": True,
}

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

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

def requirements(self):
self.requires("m4/1.4.19")

def build_requirements(self):
self.build_requires("m4/1.4.19")
datalogics-kam marked this conversation as resolved.
Show resolved Hide resolved
if hasattr(self, "settings_build") and tools.cross_building(self):
if hasattr(self, "settings_build") and cross_building(self):
self.build_requires(f"{self.name}/{self.version}")
datalogics-kam marked this conversation as resolved.
Show resolved Hide resolved

def config_options(self):
Expand All @@ -52,11 +51,10 @@ def configure(self):
if self.settings.os == "Windows":
raise ConanInvalidConfiguration("Flex package is not compatible with Windows. Consider using winflexbison instead.")

@functools.lru_cache(1)
def _configure_autotools(self):
autotools = AutoToolsBuildEnvironment(self)
def generate(self):
tc = AutotoolsToolchain(self)
yes_no = lambda v: "yes" if v else "no"
configure_args = [
tc.configure_args.extend([
"--enable-shared={}".format(yes_no(self.options.shared)),
"--enable-static={}".format(not yes_no(self.options.shared)),
"--disable-nls",
Expand All @@ -67,21 +65,25 @@ def _configure_autotools(self):
"ac_cv_func_malloc_0_nonnull=yes", "ac_cv_func_realloc_0_nonnull=yes",
# https://github.com/easybuilders/easybuild-easyconfigs/pull/5792
"ac_cv_func_reallocarray=no",
]
])
tc.generate()
tc = AutotoolsDeps(self)
tc.generate()

autotools.configure(args=configure_args, configure_dir=self._source_subfolder)
return autotools
tc = VirtualBuildEnv(self)
tc.generate(scope="build")
Copy link
Contributor

Choose a reason for hiding this comment

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

You can omit scope="build" here as it is the default.


def build(self):
autotools = self._configure_autotools()
autotools = Autotools(self)
autotools.configure()
autotools.make()

def package(self):
self.copy("COPYING", src=self._source_subfolder, dst="licenses")
autotools = self._configure_autotools()
self.copy("COPYING", src=self.source_folder, dst="licenses")
autotools = Autotools(self)
autotools.install()
tools.rmdir(os.path.join(self.package_folder, "share"))
tools.remove_files_by_mask(os.path.join(self.package_folder, "lib"), "*.la")
rmdir(self, os.path.join(self.package_folder, "share"))
rm(self, "*.la", os.path.join(self.package_folder, "lib"))

def package_info(self):
self.cpp_info.libs = ["fl"]
Expand Down
7 changes: 0 additions & 7 deletions recipes/flex/all/test_package/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
cmake_minimum_required(VERSION 3.1)
project(test_package)
datalogics-kam marked this conversation as resolved.
Show resolved Hide resolved

# Find FLEX before `conanbuildinfo.cmake` because that file will let `find_program`
# look for executables in host packages (let's hope conan 2.0 fixes this)
Comment on lines -4 to -5
Copy link
Contributor

Choose a reason for hiding this comment

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

https://cmake.org/cmake/help/latest/module/FindFLEX.html 🤔 Usually it's just variables so it's say #13511 (comment)

If this was an output of flex's CMake install we could use build_modules but I am not sure if thats the case?

Just a note this recipe is missing the cmake target definitions so you'll want to add those

Copy link
Contributor

@jwillikers jwillikers Nov 4, 2022

Choose a reason for hiding this comment

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

You should be able to inject the necessary variables in the CMakeToolchain via conf. Generate a small CMake script that sets the necessary Flex variables and add it to tools.cmake.cmaketoolchain:user_toolchain so that anything that tool_requires Flex will inherit those CMake variables. You probably will only need to set FLEX_EXECUTABLE, FLEX_LIBRARIES, and FLEX_INCLUDE_DIRS. Then maybe see what happens if you rename Conan's generated Config module to something like _Flex so that nothing will actually use it.

Alternatively, if you can add a build module that includes CMake's FindFlex module, that may also work. You'd probably have to set the same variables before including it.

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 to @prince-chrismc and @jwillikers for the insights. The suggestion to set up an environment variable gave me an idea. I went and looked at the code for FindFLEX. It's internally using find_program, find_library and find_path, all of which will use FLEX_ROOT as the first place to search, for CMake >= 3.12. See also the doc for find_library.

Turns out the following works, at least in my local testing:

  • Point FLEX_ROOT at the package.
  • Keep Conan from replacing CMake's built-in FindFLEX. For CMakeDeps, have the recipe produce nothing for CMake, and for the old generators, make the package with a different name.

find_package(FLEX REQUIRED)

include("${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
conan_basic_setup(TARGETS)

find_package(FLEX REQUIRED)

flex_target(flex_scanner basic_nr.l "${PROJECT_BINARY_DIR}/basic_nr.cpp")
Expand Down
30 changes: 22 additions & 8 deletions recipes/flex/all/test_package/conanfile.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from conans import ConanFile, CMake, tools
from conans.errors import ConanException
from conan import ConanFile
from conan.errors import ConanException
from conan.tools.build import cross_building
from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout
from conan.tools.env import VirtualBuildEnv
import os
import shutil


class TestPackageConan(ConanFile):
settings = "os", "arch", "compiler", "build_type"
generators = "cmake"
test_type = "explicit"

def requirements(self):
Expand All @@ -14,27 +17,38 @@ def requirements(self):
def build_requirements(self):
self.build_requires(self.tested_reference_str)
datalogics-kam marked this conversation as resolved.
Show resolved Hide resolved

def generate(self):
tc = CMakeToolchain(self)
# tc.variables["FLEX_ROOT"] = self.deps_cpp_info["flex"].rootpath
tc.generate()
deps = CMakeDeps(self)
deps.generate()
tc = VirtualBuildEnv(self)
tc.generate(scope="build")

def layout(self):
cmake_layout(self)

def build(self):
if not hasattr(self, "settings_build"):
# Only test location of flex executable when not cross building
flex_bin = tools.which("flex")
flex_bin = shutil.which("flex")
if not flex_bin.startswith(self.deps_cpp_info["flex"].rootpath):
raise ConanException("Wrong flex executable captured")

if not tools.cross_building(self, skip_x64_x86=True) or hasattr(self, "settings_build"):
if not cross_building(self, skip_x64_x86=True) or hasattr(self, "settings_build"):
self.run("flex --version", run_environment=not hasattr(self, "settings_build"))

print(os.environ["PATH"])
cmake = CMake(self)
cmake.definitions["FLEX_ROOT"] = self.deps_cpp_info["flex"].rootpath
cmake.configure()
cmake.build()

def test(self):
if not tools.cross_building(self, skip_x64_x86=True):
if not cross_building(self, skip_x64_x86=True):
bin_path = os.path.join("bin", "test_package")
src = os.path.join(self.source_folder, "basic_nr.txt")
self.run("{} {}".format(bin_path, src), run_environment=True)
self.run(f"{bin_path} {src}", run_environment=True)

test_yywrap = os.path.join("bin", "test_yywrap")
self.run(test_yywrap, run_environment=True)
20 changes: 20 additions & 0 deletions recipes/flex/all/test_v1_package/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.1)
project(test_package)

# Find FLEX before `conanbuildinfo.cmake` because that file will let `find_program`
# look for executables in host packages (let's hope conan 2.0 fixes this)
find_package(FLEX REQUIRED)

include("${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
conan_basic_setup(TARGETS)

find_package(FLEX REQUIRED)

flex_target(flex_scanner basic_nr.l "${PROJECT_BINARY_DIR}/basic_nr.cpp")

add_executable(${PROJECT_NAME} basic_nr.cpp)
target_include_directories(${PROJECT_NAME} PRIVATE ${FLEX_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PRIVATE ${FLEX_LIBRARIES})

add_executable(test_yywrap test_yywrap.c)
target_link_libraries(test_yywrap PRIVATE ${FLEX_LIBRARIES})
89 changes: 89 additions & 0 deletions recipes/flex/all/test_v1_package/basic_nr.l
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* This file is part of flex.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE.
*/

%option C++ noyywrap

%{
int mylineno = 0;
%}

string \"[^\n"]+\"

ws [ \t]+

alpha [A-Za-z]
dig [0-9]
name ({alpha}|{dig}|\$)({alpha}|{dig}|\_|\.|\-|\/|\$)*
num1 [-+]?{dig}+\.?([eE][-+]?{dig}+)?
num2 [-+]?{dig}*\.{dig}+([eE][-+]?{dig}+)?
number {num1}|{num2}

%%

{ws} /* skip blanks and tabs */

"/*" {
int c;

while((c = yyinput()) != 0)
{
if(c == '\n')
++mylineno;

else if(c == '*')
{
if((c = yyinput()) == '/')
break;
else
unput(c);
}
}
}

{number} std::cout << "number " << YYText() << '\n';

\n mylineno++;

{name} std::cout << "name " << YYText() << '\n';

{string} std::cout << "string " << YYText() << '\n';

%%

extern "C" {
int yylex() {return 0;}
}

#include <fstream>

int main( int argc, const char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Need an argument\n");
return 1;
}
std::ifstream ifs(argv[1]);
FlexLexer *lexer = new yyFlexLexer(ifs, std::cout);
while(lexer->yylex() != 0)
;
return 0;
}
6 changes: 6 additions & 0 deletions recipes/flex/all/test_v1_package/basic_nr.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* this is a multi line comment
still in the comment
and done */
foo = "bar"
num = 43
setting = false
40 changes: 40 additions & 0 deletions recipes/flex/all/test_v1_package/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from conans import ConanFile, CMake, tools
from conans.errors import ConanException
import os


class TestPackageConan(ConanFile):
settings = "os", "arch", "compiler", "build_type"
generators = "cmake"
test_type = "explicit"

def requirements(self):
self.requires(self.tested_reference_str)

def build_requirements(self):
self.build_requires(self.tested_reference_str)

def build(self):
if not hasattr(self, "settings_build"):
# Only test location of flex executable when not cross building
flex_bin = tools.which("flex")
if not flex_bin.startswith(self.deps_cpp_info["flex"].rootpath):
raise ConanException("Wrong flex executable captured")

if not tools.cross_building(self, skip_x64_x86=True) or hasattr(self, "settings_build"):
self.run("flex --version", run_environment=not hasattr(self, "settings_build"))

print(os.environ["PATH"])
cmake = CMake(self)
cmake.definitions["FLEX_ROOT"] = self.deps_cpp_info["flex"].rootpath
cmake.configure()
cmake.build()

def test(self):
if not tools.cross_building(self, skip_x64_x86=True):
bin_path = os.path.join("bin", "test_package")
src = os.path.join(self.source_folder, "basic_nr.txt")
self.run("{} {}".format(bin_path, src), run_environment=True)

test_yywrap = os.path.join("bin", "test_yywrap")
self.run(test_yywrap, run_environment=True)
10 changes: 10 additions & 0 deletions recipes/flex/all/test_v1_package/test_yywrap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <stdio.h>

int yywrap(void);
int yylex(void) {
return 0;
}

int main() {
printf("yywrap() returned: %d.\n", yywrap());
}