Commit c3203a3b authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Merge branch 'development/update-ci-split-component' into 'next'

Add compilers in the CI + split Pylene-core into components

See merge request !120
parents d611ce4f 24f26b4b
Pipeline #30489 passed with stages
in 28 minutes and 33 seconds
...@@ -13,6 +13,7 @@ variables: ...@@ -13,6 +13,7 @@ variables:
FEDORA_RAWHIDE: "${CI_REGISTRY}/olena/pylene-dockers/fedora-rawhide" FEDORA_RAWHIDE: "${CI_REGISTRY}/olena/pylene-dockers/fedora-rawhide"
FEDORA_31: "${CI_REGISTRY}/olena/pylene-dockers/fedora-31" FEDORA_31: "${CI_REGISTRY}/olena/pylene-dockers/fedora-31"
FEDORA_32: "${CI_REGISTRY}/olena/pylene-dockers/fedora-32" FEDORA_32: "${CI_REGISTRY}/olena/pylene-dockers/fedora-32"
FEDORA_34: "${CI_REGISTRY}/olena/pylene-dockers/fedora-34"
CMAKE_BUILD_PARALLEL_LEVEL: 6 CMAKE_BUILD_PARALLEL_LEVEL: 6
CONAN_UPLOAD: "https://artifactory.lrde.epita.fr/artifactory/api/conan/lrde-public@True" CONAN_UPLOAD: "https://artifactory.lrde.epita.fr/artifactory/api/conan/lrde-public@True"
CONAN_USERNAME: "lrde" CONAN_USERNAME: "lrde"
...@@ -92,6 +93,9 @@ distcheck-linux-gcc9-release: ...@@ -92,6 +93,9 @@ distcheck-linux-gcc9-release:
CXX: "g++" CXX: "g++"
CC: "gcc" CC: "gcc"
CONAN_PROFILE: "gcc-9" CONAN_PROFILE: "gcc-9"
rules:
- if: $CI_MERGE_REQUEST_ID
- if: $CI_COMMIT_BRANCH == "master"
distcheck-linux-gcc9-debug-asan-ubsan: distcheck-linux-gcc9-debug-asan-ubsan:
...@@ -104,6 +108,12 @@ distcheck-linux-gcc9-debug-asan-ubsan: ...@@ -104,6 +108,12 @@ distcheck-linux-gcc9-debug-asan-ubsan:
CONAN_PROFILE: "gcc-9" CONAN_PROFILE: "gcc-9"
CCFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined CCFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CXXFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined CXXFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
rules:
- if: $CI_COMMIT_BRANCH == "master"
- if: $CI_MERGE_REQUEST_ID
when: never
- when: manual
allow_failure: true
distcheck-linux-clang10-release: distcheck-linux-clang10-release:
<<: *distcheck-linux-base <<: *distcheck-linux-base
...@@ -152,6 +162,51 @@ distcheck-linux-gcc10-debug: ...@@ -152,6 +162,51 @@ distcheck-linux-gcc10-debug:
CXXFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined CXXFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CONAN_PROFILE: "gcc-10" CONAN_PROFILE: "gcc-10"
distcheck-linux-gcc11-release:
<<: *distcheck-linux-base
image: ${FEDORA_34}
variables:
PYLENE_CONFIGURATION: "Release"
CXX: "g++"
CC: "gcc"
CONAN_PROFILE: "gcc-11"
rules:
- if: $CI_MERGE_REQUEST_ID
- if: $CI_COMMIT_BRANCH == "master"
distcheck-linux-gcc11-debug:
<<: *distcheck-linux-base
image: ${FEDORA_34}
variables:
PYLENE_CONFIGURATION: "Debug"
CXX: "g++"
CC: "gcc"
CCFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CXXFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CONAN_PROFILE: "gcc-11"
distcheck-linux-clang12-release:
<<: *distcheck-linux-base
image: ${FEDORA_34}
variables:
PYLENE_CONFIGURATION: "Release"
CXX: "clang++"
CC: "clang"
CONAN_PROFILE: "clang-12"
rules:
- if: $CI_MERGE_REQUEST_ID
- if: $CI_COMMIT_BRANCH == "master"
distcheck-linux-clang12-debug:
<<: *distcheck-linux-base
image: ${FEDORA_34}
variables:
PYLENE_CONFIGURATION: "Debug"
CXX: "clang++"
CC: "clang"
CCFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CXXFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CONAN_PROFILE: "clang-12"
distcheck-linux-coverage: distcheck-linux-coverage:
<<: *distcheck-linux-base <<: *distcheck-linux-base
...@@ -273,3 +328,14 @@ package clang-10: ...@@ -273,3 +328,14 @@ package clang-10:
variables: variables:
CONAN_BASE_PROFILE: clang-10 CONAN_BASE_PROFILE: clang-10
package gcc-11:
extends: .package
image: ${FEDORA_34}
variables:
CONAN_BASE_PROFILE: gcc-11
package clang-12:
extends: .package
image: ${FEDORA_34}
variables:
CONAN_BASE_PROFILE: clang-12
\ No newline at end of file
...@@ -132,7 +132,7 @@ target_link_libraries(Pylene INTERFACE Pylene::Core) ...@@ -132,7 +132,7 @@ target_link_libraries(Pylene INTERFACE Pylene::Core)
include(GNUInstallDirs) include(GNUInstallDirs)
set(PyleneTargets Pylene Pylene-core Pylene-bp) set(PyleneTargets Pylene Pylene-core Pylene-bp Pylene-io-freeimage)
if (TARGET Pylene-numpy) if (TARGET Pylene-numpy)
list(APPEND PyleneTargets Pylene-numpy) list(APPEND PyleneTargets Pylene-numpy)
endif() endif()
......
...@@ -39,7 +39,7 @@ add_library(BenchImpl ...@@ -39,7 +39,7 @@ add_library(BenchImpl
set_target_properties(BenchImpl PROPERTIES COMPILE_FLAGS ${STANDALONE_COMPILE_FLAGS}) set_target_properties(BenchImpl PROPERTIES COMPILE_FLAGS ${STANDALONE_COMPILE_FLAGS})
target_include_directories(BenchImpl PUBLIC include) target_include_directories(BenchImpl PUBLIC include)
target_link_libraries(BenchImpl PUBLIC Pylene::Pylene) target_link_libraries(BenchImpl PUBLIC Pylene::Core Pylene::IO-freeimage)
add_subdirectory(tests) add_subdirectory(tests)
......
...@@ -16,6 +16,7 @@ class Pylene(ConanFile): ...@@ -16,6 +16,7 @@ class Pylene(ConanFile):
"shared": False, "shared": False,
"fPIC": False, "fPIC": False,
"gtest:shared": False, "gtest:shared": False,
"boost:header_only": True,
} }
generators = [ "cmake", "cmake_paths", "cmake_find_package" ] generators = [ "cmake", "cmake_paths", "cmake_find_package" ]
...@@ -24,10 +25,6 @@ class Pylene(ConanFile): ...@@ -24,10 +25,6 @@ class Pylene(ConanFile):
build_requires = [ build_requires = [
"gtest/[>=1.10]", "gtest/[>=1.10]",
"benchmark/[>=1.5.0]", "benchmark/[>=1.5.0]",
# For now boost is too heavy and is not based on components
# Such a dependancy brings linktime overhead because too many libraries are linked
# "boost/1.73.0"
] ]
requires = [ requires = [
...@@ -35,6 +32,7 @@ class Pylene(ConanFile): ...@@ -35,6 +32,7 @@ class Pylene(ConanFile):
"fmt/6.0.0", "fmt/6.0.0",
"tbb/2020.0", "tbb/2020.0",
"xsimd/7.4.6", "xsimd/7.4.6",
"boost/1.75.0"
] ]
def _build_python(self): def _build_python(self):
...@@ -66,13 +64,22 @@ class Pylene(ConanFile): ...@@ -66,13 +64,22 @@ class Pylene(ConanFile):
self.cpp_info.names["cmake_find_package"] = "Pylene" self.cpp_info.names["cmake_find_package"] = "Pylene"
self.cpp_info.names["cmake_find_package_multi"] = "Pylene" self.cpp_info.names["cmake_find_package_multi"] = "Pylene"
self.cpp_info.components["Core"].system_libs.append("freeimage") # Core component
self.cpp_info.components["Core"].names["cmake_find_package"] = "Core" self.cpp_info.components["Core"].names["cmake_find_package"] = "Core"
self.cpp_info.components["Core"].names["cmake_find_package_multi"] = "Core" self.cpp_info.components["Core"].names["cmake_find_package_multi"] = "Core"
self.cpp_info.components["Core"].libs = ["Pylene-core"] self.cpp_info.components["Core"].libs = ["Pylene-core"]
self.cpp_info.components["Core"].includedirs = ["include"] self.cpp_info.components["Core"].includedirs = ["include"]
self.cpp_info.components["Core"].requires = ["range-v3::range-v3", "fmt::fmt", "tbb::tbb", "xsimd::xsimd"] self.cpp_info.components["Core"].requires = ["range-v3::range-v3", "fmt::fmt", "tbb::tbb", "xsimd::xsimd", "boost::headers"]
# IO component
self.cpp_info.components["IO-freeimage"].system_libs.append("freeimage")
self.cpp_info.components["IO-freeimage"].names["cmake_find_package"] = "IO-freeimage"
self.cpp_info.components["IO-freeimage"].names["cmake_find_package_multi"] = "IO-freeimage"
self.cpp_info.components["IO-freeimage"].libs = ["Pylene-io-freeimage"]
self.cpp_info.components["IO-freeimage"].includedirs = ["include"]
self.cpp_info.components["IO-freeimage"].requires = ["Core"]
# Pylene-numpy component
if self._build_python(): if self._build_python():
self.cpp_info.components["Pylene-numpy"].names["cmake_find_pakage_multi"] = "Pylene-numpy" self.cpp_info.components["Pylene-numpy"].names["cmake_find_pakage_multi"] = "Pylene-numpy"
self.cpp_info.components["Pylene-numpy"].names["cmake_find_pakage"] = "Pylene-numpy" self.cpp_info.components["Pylene-numpy"].names["cmake_find_pakage"] = "Pylene-numpy"
......
find_package(Doxygen) find_package(Doxygen)
find_package(Sphinx) find_package(Sphinx)
find_package(Boost COMPONENTS program_options REQUIRED)
add_subdirectory(source/snippets) add_subdirectory(source/snippets)
......
...@@ -73,7 +73,7 @@ target_link_libraries(doc-lib Pylene::Core) ...@@ -73,7 +73,7 @@ target_link_libraries(doc-lib Pylene::Core)
link_libraries(Pylene::Core) link_libraries(Pylene::Core Pylene::IO-freeimage)
link_libraries(doc-lib) link_libraries(doc-lib)
add_executable(alphatree_example alphatree_example.cpp) add_executable(alphatree_example alphatree_example.cpp)
...@@ -89,11 +89,4 @@ add_executable(cdt cdt.cpp) ...@@ -89,11 +89,4 @@ add_executable(cdt cdt.cpp)
add_executable(first_start_1 first_start_1.cpp) add_executable(first_start_1 first_start_1.cpp)
add_executable(intro-1 intro-1.cpp) add_executable(intro-1 intro-1.cpp)
target_compile_definitions(erosion-cli PRIVATE BOOST_ALL_NO_LIB) target_compile_definitions(erosion-cli PRIVATE BOOST_ALL_NO_LIB)
\ No newline at end of file
# for program_options, need to separate CONAN and regular FindBoost
if (TARGET Boost::program_options)
target_link_libraries(erosion-cli PRIVATE Boost::program_options)
elseif (TARGET Boost::Boost)
target_link_libraries(erosion-cli PRIVATE Boost::Boost)
endif()
\ No newline at end of file
#include <mln/core/image/ndimage.hpp> #include <mln/core/image/ndimage.hpp>
#include <mln/core/se/disc.hpp>
#include <mln/core/se/rect2d.hpp> #include <mln/core/se/rect2d.hpp>
#include <mln/morpho/closing.hpp> #include <mln/morpho/closing.hpp>
#include <mln/morpho/dilation.hpp> #include <mln/morpho/dilation.hpp>
#include <mln/morpho/erosion.hpp> #include <mln/morpho/erosion.hpp>
#include <mln/morpho/opening.hpp>
#include <mln/morpho/median_filter.hpp>
#include <mln/morpho/gradient.hpp> #include <mln/morpho/gradient.hpp>
#include <mln/morpho/median_filter.hpp>
#include <mln/morpho/opening.hpp>
#include <mln/io/imread.hpp> #include <mln/io/imread.hpp>
#include <mln/io/imsave.hpp> #include <mln/io/imsave.hpp>
#include <cstdlib>
#include <algorithm>
#include <boost/program_options.hpp>
#include <cctype>
#include <iostream> #include <iostream>
#include <string> #include <string>
namespace po = boost::program_options; enum class morpho_op_type
enum se_type
{
kSquare,
kDisc,
kDiamond
};
int tolower_safe(int c)
{
return std::tolower(static_cast<unsigned char>(c));
}
std::istream& operator>>(std::istream& in, se_type& se)
{ {
std::string token; kErosion = 0,
in >> token;
std::transform(token.begin(), token.end(), token.begin(), tolower_safe);
if (token == "square")
se = kSquare;
else if (token == "disc")
se = kDisc;
else if (token == "diamond")
se = kDiamond;
else
throw po::invalid_option_value("Invalid SE");
return in;
}
enum morpho_op_type
{
kErosion,
kDilation, kDilation,
kOpening, kOpening,
kClosing, kClosing,
...@@ -62,110 +29,113 @@ enum morpho_op_type ...@@ -62,110 +29,113 @@ enum morpho_op_type
kExternalGradient kExternalGradient
}; };
std::istream& operator>>(std::istream& in, morpho_op_type& se) enum class se_type
{
kSquare = 0,
kDisc
};
morpho_op_type get_morpho_op(char* arg)
{ {
std::string token; const std::string str(arg);
in >> token; if (str == "erosion")
std::transform(token.begin(), token.end(), token.begin(), tolower_safe); return morpho_op_type::kErosion;
if (token == "erosion") else if (str == "dilation")
se = kErosion; return morpho_op_type::kDilation;
else if (token == "dilation") else if (str == "opening")
se = kDilation; return morpho_op_type::kOpening;
else if (token == "opening") else if (str == "closing")
se = kOpening; return morpho_op_type::kClosing;
else if (token == "closing") else if (str == "median")
se = kClosing; return morpho_op_type::kMedian;
else if (token == "median") else if (str == "gradient")
se = kMedian; return morpho_op_type::kGradient;
else if (token == "gradient") else if (str == "int_gradient")
se = kGradient; return morpho_op_type::kInternalGradient;
else if (token == "ext_gradient") else if (str == "ext_gradient")
se = kExternalGradient; return morpho_op_type::kExternalGradient;
else if (token == "int_gradient")
se = kInternalGradient;
else else
throw po::invalid_option_value("Invalid Operator"); throw std::invalid_argument("Invalid input morphological operation");
return in;
} }
se_type get_se(char* arg)
struct exec_params_type
{ {
se_type se; const std::string str(arg);
morpho_op_type op; if (str == "square")
int size; return se_type::kSquare;
}; else if (str == "disk")
return se_type::kDisc;
else
throw std::invalid_argument("Invalid input structuring element");
}
int main(int argc, char** argv) template <typename SE>
mln::image2d<std::uint8_t> process(mln::image2d<std::uint8_t> img, morpho_op_type op, SE se)
{ {
po::options_description desc("Allowed options"); using namespace mln::morpho;
po::positional_options_description p; switch (op)
p.add("operator", 1).add("se", 1).add("size", 1).add("input", 1).add("output", 1);
desc.add_options()("help", "produce help message")("operator", po::value<morpho_op_type>()->required(), "")(
"se", po::value<se_type>()->required(), "")("size", po::value<int>()->required(), "Size of the SE.")(
"input", po::value<std::string>()->required(),
"Input image (8u or rgb8)")("output", po::value<std::string>()->required(), "Output image (8u or rgb8)");
po::variables_map vm;
int size;
try
{ {
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm); case morpho_op_type::kErosion:
po::notify(vm); return erosion(img, se);
size = vm["size"].as<int>(); break;
if (size < 1 || size % 2 == 0) case morpho_op_type::kDilation:
{ return dilation(img, se);
std::cerr << "Size must be positive and odd.\n"; break;
return 1; case morpho_op_type::kOpening:
} return opening(img, se);
break;
case morpho_op_type::kClosing:
return closing(img, se);
break;
case morpho_op_type::kMedian:
return median_filter(img, se, mln::extension::bm::mirror{});
break;
case morpho_op_type::kGradient:
return gradient(img, se);
break;
case morpho_op_type::kInternalGradient:
return internal_gradient(img, se);
break;
case morpho_op_type::kExternalGradient:
return external_gradient(img, se);
break;
} }
catch (...) return mln::image2d<std::uint8_t>();
}
int main(int argc, char* argv[])
{
using namespace mln;
if (argc != 6)
{ {
std::cout << "Usage: " << argv[0] std::cout << "Usage: " << argv[0]
<< " [-h,--help] OPERATOR SE size input output\n" << " OPERATOR SE size input output\n"
"OPERATOR\t Morphological operation to perform [erosion | dilation | opening | closing | median | gradient | ext_gradient | int_gradient]\n" "OPERATOR\t Morphological operation to perform [erosion | dilation | opening | closing | median | "
"SE\t Structuring element to use [square | disc | diamond]\n" "gradient | ext_gradient | int_gradient]\n"
"SE\t Structuring element to use [square | disc]\n"
"size\t Size of the SE\n" "size\t Size of the SE\n"
"input\t Input image (u8)\n" "input\t Input image (u8)\n"
"output\t Output image (u8)\n"; "output\t Output image (u8)\n";
return 1; return 1;
} }
mln::image2d<uint8_t> input, output; const auto input_op = get_morpho_op(argv[1]);
mln::io::imread(vm["input"].as<std::string>(), input); const auto input_se = get_se(argv[2]);
const auto size = std::atoi(argv[3]);
mln::image2d<std::uint8_t> img;
mln::io::imread(argv[4], img);
mln::se::rect2d nbh(size, size); if (size < 1 || size % 2 == 0)
throw std::invalid_argument("Structuring element size must be positive and odd");
switch (vm["operator"].as<morpho_op_type>()) mln::image2d<std::uint8_t> out;
{ if (input_se == se_type::kDisc)
case kErosion: out = process(img, input_op, se::disc(size));
output = mln::morpho::erosion(input, nbh); else
break; out = process(img, input_op, se::rect2d(size, size));
case kDilation:
output = mln::morpho::dilation(input, nbh);
break;
case kOpening:
output = mln::morpho::opening(input, nbh);
break;
case kClosing:
output = mln::morpho::closing(input, nbh);
break;
case kMedian:
output = mln::morpho::median_filter(input, nbh, mln::extension::bm::mirror{});
break;
case kGradient:
output = mln::morpho::gradient(input, nbh);
break;
case kExternalGradient:
output = mln::morpho::external_gradient(input, nbh);
break;
case kInternalGradient:
output = mln::morpho::internal_gradient(input, nbh);
break;
}
mln::io::imsave(output, vm["output"].as<std::string>()); mln::io::imsave(out, argv[5]);
} return 0;
}
\ No newline at end of file
...@@ -42,8 +42,6 @@ target_include_directories(Pylene-core PUBLIC ...@@ -42,8 +42,6 @@ target_include_directories(Pylene-core PUBLIC
target_include_directories(Pylene-core SYSTEM PUBLIC $<BUILD_INTERFACE:${Boost_INCLUDE_DIRS}>) target_include_directories(Pylene-core SYSTEM PUBLIC $<BUILD_INTERFACE:${Boost_INCLUDE_DIRS}>)
target_link_libraries(Pylene-core PUBLIC range-v3::range-v3 xsimd::xsimd) target_link_libraries(Pylene-core PUBLIC range-v3::range-v3 xsimd::xsimd)
target_link_libraries(Pylene-core PUBLIC fmt::fmt) target_link_libraries(Pylene-core PUBLIC fmt::fmt)
target_link_libraries(Pylene-core PRIVATE FreeImage::FreeImage)
# Set sources # Set sources
#file(GLOB_RECURSE sources "include/mln/*.hpp") #file(GLOB_RECURSE sources "include/mln/*.hpp")
...@@ -79,10 +77,7 @@ target_sources(Pylene-core PRIVATE ...@@ -79,10 +77,7 @@ target_sources(Pylene-core PRIVATE
src/core/se/rect2d.cpp src/core/se/rect2d.cpp
src/core/trace.cpp src/core/trace.cpp
src/core/traverse2d.cpp src/core/traverse2d.cpp
src/io/freeimage_plugin.cpp
src/io/imprint.cpp src/io/imprint.cpp
src/io/imread.cpp
src/io/io.cpp
src/morpho/block_running_max.cpp src/morpho/block_running_max.cpp
src/morpho/component_tree.cpp src/morpho/component_tree.cpp
src/morpho/hvector.cpp src/morpho/hvector.cpp
...@@ -93,12 +88,27 @@ target_sources(Pylene-core PRIVATE ...@@ -93,12 +88,27 @@ target_sources(Pylene-core PRIVATE
src/morpho/filters2d.cpp src/morpho/filters2d.cpp
) )
add_library(Pylene-io-freeimage)
add_library(Pylene::IO-freeimage ALIAS Pylene-io-freeimage)
target_sources(Pylene-io-freeimage PRIVATE
src/io/freeimage_plugin.cpp
src/io/imread.cpp
src/io/io.cpp
)
target_compile_features(Pylene-io-freeimage PUBLIC cxx_std_20)
target_include_directories(Pylene-io-freeimage PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(Pylene-io-freeimage PUBLIC FreeImage::FreeImage Pylene-core)
# Compiler configurations # Compiler configurations
target_compile_features(Pylene-core PUBLIC cxx_std_20) target_compile_features(Pylene-core PUBLIC cxx_std_20)
if (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) if (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0)
target_compile_options(Pylene-bp PUBLIC -fconcepts) target_compile_options(Pylene-bp PUBLIC -fconcepts)
target_compile_options(Pylene-core PUBLIC -fconcepts) target_compile_options(Pylene-core PUBLIC -fconcepts)
target_compile_options(Pylene-io-freeimage PUBLIC -fconcepts)
endif() endif()
# IDE configuration # IDE configuration
......