diff --git a/.github/workflows/conan.yml b/.github/workflows/conan.yml index 81c1857..e7a21ba 100644 --- a/.github/workflows/conan.yml +++ b/.github/workflows/conan.yml @@ -15,10 +15,11 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - name: install conan - uses: turtlebrowser/get-conan@main - - - name: create package - run: | - conan create -pr:a conan/windows_release . --build=missing + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install Conan + run: python -m pip install "conan>=2.10,<3" + - name: Create package + run: conan create -pr:a conan/windows_release . --build=missing diff --git a/CMakeLists.txt b/CMakeLists.txt index 3069537..05984fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,12 @@ -cmake_minimum_required(VERSION 3.11) +cmake_minimum_required(VERSION 3.16) project(uca C) +include(CTest) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + # Prefer a Python 3 interpreter; keep compatibility with older CMake versions. if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.12") find_package(Python3 COMPONENTS Interpreter REQUIRED) @@ -13,7 +19,7 @@ endif() option(WITH_PYTHON_MULTITHREADING "Enable Python multithreading support" ON) option(WITH_GIR "Build introspection files" ON) option(WITH_TIFF "Support loading tiff files" ON) -option(WITH_GUI "Build the GUI tools" ON) +option(WITH_GUI "Build the deprecated GTK 2 GUI tools" OFF) option(WITH_TOOLS "Build the CLI tools" ON) option(USE_FIND_PACKAGE_FOR_GLIB "Use find_package instead of pkg-config to find GLib dependencies" OFF) @@ -25,10 +31,10 @@ else() message(STATUS "Using pkg-config for GLib") find_package(PkgConfig) find_program(GLIB2_MKENUMS glib-mkenums REQUIRED) - pkg_check_modules(GLIB2 glib-2.0>=2.38 REQUIRED) - pkg_check_modules(GOBJECT2 gobject-2.0>=2.38 REQUIRED) - pkg_check_modules(GMODULE2 gmodule-2.0>=2.38 REQUIRED) - pkg_check_modules(GIO2 gio-2.0>=2.38 REQUIRED) + pkg_check_modules(GLIB2 glib-2.0>=2.56 REQUIRED) + pkg_check_modules(GOBJECT2 gobject-2.0>=2.56 REQUIRED) + pkg_check_modules(GMODULE2 gmodule-2.0>=2.56 REQUIRED) + pkg_check_modules(GIO2 gio-2.0>=2.56 REQUIRED) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(PkgConfigVars) @@ -50,7 +56,7 @@ else() endif() if (WITH_TIFF) - find_package(TIFF REQUIRED) + find_package(TIFF 4.3 REQUIRED) set(HAVE_LIBTIFF "1") endif() @@ -118,15 +124,15 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/package.sh.in #{{{ Common dependencies find_program(GLIB2_MKENUMS glib-mkenums REQUIRED) -set(GLIB_VERSION_MIN_REQUIRED "GLIB_VERSION_2_38") -set(GLIB_VERSION_MAX_ALLOWED "GLIB_VERSION_2_38") +set(GLIB_VERSION_MIN_REQUIRED "GLIB_VERSION_2_56") +set(GLIB_VERSION_MAX_ALLOWED "GLIB_VERSION_2_56") link_directories(${GLIB2_LIBRARY_DIRS}) #}}} #{{{ Common definitions -if (GNU) - add_definitions("-std=c99 -Wall -fPIC") +if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + add_compile_options(-Wall -Wextra) endif() #}}} #{{{ Subdirectories diff --git a/bin/gui/CMakeLists.txt b/bin/gui/CMakeLists.txt index fd29129..bc2e778 100644 --- a/bin/gui/CMakeLists.txt +++ b/bin/gui/CMakeLists.txt @@ -1,7 +1,8 @@ find_package(PkgConfig) +message(DEPRECATION "The GTK 2 camera-control GUI is deprecated and disabled by default") find_program(GLIB_COMPILE_RESOURCES glib-compile-resources REQUIRED) -pkg_check_modules(GTK2 gtk+-2.0>=2.22) +pkg_check_modules(GTK2 gtk+-2.0>=2.24) pkg_check_modules(GTHREAD2 gthread-2.0) if (GTK2_FOUND) diff --git a/bin/gui/meson.build b/bin/gui/meson.build index eb2424d..a574a1f 100644 --- a/bin/gui/meson.build +++ b/bin/gui/meson.build @@ -1,7 +1,8 @@ cc = meson.get_compiler('c') libm = cc.find_library('m') -gtk_dep = dependency('gtk+-2.0', required: false) +warning('The GTK 2 camera-control GUI is deprecated and disabled by default') +gtk_dep = dependency('gtk+-2.0', version: '>= 2.24', required: get_option('gui')) gthread_dep = dependency('gthread-2.0') if gtk_dep.found() diff --git a/bin/meson.build b/bin/meson.build index 12810f5..22cf107 100644 --- a/bin/meson.build +++ b/bin/meson.build @@ -1,2 +1,4 @@ -subdir('gui') +if not get_option('gui').disabled() + subdir('gui') +endif subdir('tools') diff --git a/bin/tools/CMakeLists.txt b/bin/tools/CMakeLists.txt index bca0537..00a0191 100644 --- a/bin/tools/CMakeLists.txt +++ b/bin/tools/CMakeLists.txt @@ -12,7 +12,10 @@ set(BINARIES "benchmark" "gen-doc" "info" "grab") foreach (BINARY ${BINARIES}) add_executable(uca-${BINARY} ${BINARY}.c common.c) - target_link_libraries(uca-${BINARY} PUBLIC uca TIFF::TIFF) + target_link_libraries(uca-${BINARY} PUBLIC uca) + if (WITH_TIFF) + target_link_libraries(uca-${BINARY} PUBLIC TIFF::TIFF) + endif () endforeach () if (GNU) diff --git a/bin/tools/grab.c b/bin/tools/grab.c index 5074387..d002e42 100644 --- a/bin/tools/grab.c +++ b/bin/tools/grab.c @@ -86,25 +86,27 @@ write_tiff (UcaRingBuffer *buffer, tif = TIFFOpen (opts->filename, "w"); n_frames = uca_ring_buffer_get_num_blocks (buffer); - rows_per_strip = TIFFDefaultStripSize (tif, (guint32) - 1); bytes_per_pixel = get_bytes_per_pixel (bits_per_pixel); bits_per_sample = bits_per_pixel > 8 ? 16 : 8; - /* Write multi page TIFF file */ - TIFFSetField (tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE); - + /* Write multi page TIFF file. libtiff needs the image geometry before it + * can calculate a valid default strip size, and TIFFWriteDirectory resets + * the directory fields for the following page. */ for (guint i = 0; i < n_frames; i++) { gpointer data; gsize offset = 0; data = uca_ring_buffer_get_read_pointer (buffer); + TIFFSetField (tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE); TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, width); TIFFSetField (tif, TIFFTAG_IMAGELENGTH, height); TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample); TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + rows_per_strip = TIFFDefaultStripSize (tif, (guint32) -1); TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rows_per_strip); TIFFSetField (tif, TIFFTAG_PAGENUMBER, i, n_frames); diff --git a/bin/tools/meson.build b/bin/tools/meson.build index b44aa44..77b6d05 100644 --- a/bin/tools/meson.build +++ b/bin/tools/meson.build @@ -1,7 +1,7 @@ cc = meson.get_compiler('c') libm = cc.find_library('m') -tiff_dep = dependency('libtiff-4', required: false) +tiff_dep = dependency('libtiff-4', version: '>= 4.3', required: false) config = configuration_data() config.set('HAVE_LIBTIFF', tiff_dep.found()) diff --git a/conan.lock b/conan.lock new file mode 100644 index 0000000..0f70fb2 --- /dev/null +++ b/conan.lock @@ -0,0 +1,37 @@ +{ + "version": "0.5", + "requires": [ + "zstd/1.5.7#b68ca8e3de04ba5957761751d1d661f4%1760955092.069", + "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503", + "xz_utils/5.8.3#a8432fead347c69d8b2737c35f936132%1775752656.4", + "pcre2/10.42#a0b25dffca18f5987304619e9c93fb44%1783075188.143", + "libtiff/4.7.2#7283595b6a38d0781953fa63881e0e92%1783597163.469", + "libselinux/3.6#5a78ff6ae5034eeaac8da723361a8ce4%1717655459.344", + "libmount/2.39.2#64850679fa9d5ead878e5076b0cab2a8%1705591482.625", + "libjpeg/9f#8b1b89da851ad9172565c35f14e23c4c%1780476337.068", + "libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1774021608.288", + "libgettext/0.22#b09eea019e19b9b9c46d8f1da7d75444%1765809130.834", + "libffi/3.4.8#a045c00fb26779635e3bed40e80c5254%1753360042.396", + "glib/2.86.5#2886b0810a99a7d54b313db5d214f221%1783335578.429", + "elfutils/0.190#8e8c47bd9b2578e29a0341b09b977be8%1739188192.515", + "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1762886692.465" + ], + "build_requires": [ + "pkgconf/2.5.1#93c2051284cba1279494a43a4fcfeae2%1757684701.089", + "pkgconf/2.2.0#4ac315b50ef734072b00ff3aacbf52bf%1755505628.021", + "ninja/1.13.2#c8c5dc2a52ed6e4e42a66d75b4717ceb%1764096931.974", + "msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649", + "meson/1.10.2#9d2d10681fe7fe61c788c58626c89b25%1775558003.754", + "m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259", + "libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1774021608.288", + "gnu-config/cci.20210814#466e9d4d7779e1c142443f7ea44b4284%1762363589.329", + "gettext/0.22.5#2f6edba611e8db1a8a18488b116e8c2a%1765299121.036", + "flex/2.6.4#efa781fc5088b47c895bd4eef6911f2e%1761560242.855", + "cmake/4.4.0#a5dd5d73bd9fc56b0fc3163a04573dc8%1784051561.926", + "bison/3.8.2#c3490cbe0078b6fd3eb4cf5ed64144dc%1755688988.644", + "automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56", + "autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86" + ], + "python_requires": [], + "config_requires": [] +} diff --git a/conanfile.py b/conanfile.py index ffd243e..ab3827b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,3 +1,5 @@ +import os + from conan import ConanFile from conan.tools.cmake import cmake_layout, CMakeToolchain, CMake from conan.tools.microsoft.visual import is_msvc @@ -5,19 +7,23 @@ class UcaConan(ConanFile): name = "libuca" - version = "2.3.0" - license = "LGPL-2.1" + version = "2.4.0" + license = "LGPL-2.1-or-later" author = "Marius Elvert marius.elvert@softwareschneiderei.de" url = "https://github.com/ufo-kit/libuca" description = "GLib-based C library for unified camera access ." topics = ("utilities",) settings = "os", "compiler", "build_type", "arch" - options = {"shared": [True, False]} - default_options = {"shared": True} + options = { + "shared": [True, False], + "with_tiff": [True, False], + } + default_options = { + "shared": True, + "with_tiff": True, + } generators = "CMakeDeps" - exports_sources = "src/*", "include/*", "test/*", "bin/*", "plugins/*", "CMakeLists.txt", "package.sh.in" - tool_requires = "glib/2.81.0", - + exports_sources = "src/*", "test/*", "bin/*", "plugins/*", "cmake/*", "CMakeLists.txt", "package.sh.in", "COPYING" def configure(self): # These are not applicable for C libraries self.settings.rm_safe("compiler.libcxx") @@ -26,16 +32,23 @@ def configure(self): self.options["glib"].shared = True def requirements(self): - self.requires("libtiff/4.4.0") - self.requires("glib/2.81.0", transitive_headers=True) + if self.options.with_tiff: + self.requires("libtiff/4.7.2") + self.requires("glib/2.86.5", transitive_headers=True) def generate(self): tc = CMakeToolchain(self) + # glib-mkenums is a Python script from the GLib library package. + # Host dependencies are not necessarily on the build PATH (Windows). + # CMake invokes this script using the build machine's Python interpreter. + tc.variables["GLIB2_MKENUMS"] = os.path.join( + self.dependencies["glib"].package_folder, "bin", "glib-mkenums" + ).replace("\\", "/") tc.variables["WITH_PYTHON_MULTITHREADING"] = False tc.variables["WITH_GIR"] = False tc.variables["WITH_GUI"] = False tc.variables["WITH_TOOLS"] = True - tc.variables["WITH_TIFF"] = True + tc.variables["WITH_TIFF"] = bool(self.options.with_tiff) tc.variables["USE_FIND_PACKAGE_FOR_GLIB"] = True tc.generate() diff --git a/meson.build b/meson.build index eb6de12..9572583 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,6 @@ project('libuca', 'c', - version: '2.4.0' + version: '2.4.0', + default_options: ['c_std=c11', 'warning_level=2'] ) version = meson.project_version() @@ -10,10 +11,10 @@ version_patch = components[2] gnome = import('gnome') -glib_dep = dependency('glib-2.0', version: '>= 2.38') -gio_dep = dependency('gio-2.0', version: '>= 2.38') -gobject_dep = dependency('gobject-2.0', version: '>= 2.38') -gmodule_dep = dependency('gmodule-2.0', version: '>= 2.38') +glib_dep = dependency('glib-2.0', version: '>= 2.56') +gio_dep = dependency('gio-2.0', version: '>= 2.56') +gobject_dep = dependency('gobject-2.0', version: '>= 2.56') +gmodule_dep = dependency('gmodule-2.0', version: '>= 2.56') deps = [glib_dep, gio_dep, gobject_dep, gmodule_dep] diff --git a/meson_options.txt b/meson_options.txt index 3a387d8..8d8077c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,3 +11,7 @@ option('python', type : 'string', value : 'python3', description : 'Name of python executable') + +option('gui', + type : 'feature', value : 'disabled', + description : 'Build the deprecated GTK 2 GUI tools') diff --git a/plugins/file/meson.build b/plugins/file/meson.build index 47273dd..8b75240 100644 --- a/plugins/file/meson.build +++ b/plugins/file/meson.build @@ -1,4 +1,4 @@ -tiff_dep = dependency('libtiff-4', required: false) +tiff_dep = dependency('libtiff-4', version: '>= 4.3', required: false) if tiff_dep.found() shared_library('ucafile', diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 238b334..3660649 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -151,7 +151,9 @@ set(CPACK_PACKAGE_VERSION_MINOR ${UCA_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${UCA_VERSION_PATCH}) set(VERSION ${UCA_VERSION_STRING}) -set(CPACK_GENERATOR "RPM;") +set(CPACK_GENERATOR "TGZ" CACHE STRING + "Binary package generator(s), for example TGZ, DEB, or RPM") +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_IGNORE_FILES ".git" "tags" ".bzr" ".swp") diff --git a/src/meson.build b/src/meson.build index 84b004a..ff1428f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -17,8 +17,8 @@ plugindir = '@0@/@1@/uca'.format(get_option('prefix'), get_option('libdir')) conf = configuration_data() conf.set_quoted('UCA_PLUGINDIR', plugindir) -conf.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_38') -conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_38') +conf.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_56') +conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_56') if python_dep.found() conf.set('WITH_PYTHON_MULTITHREADING', get_option('with_python_multithreading')) diff --git a/src/uca-plugin-manager.c b/src/uca-plugin-manager.c index b37a3e3..071a162 100644 --- a/src/uca-plugin-manager.c +++ b/src/uca-plugin-manager.c @@ -51,12 +51,13 @@ struct _UcaPluginManagerPrivate { #ifdef _WIN32 -static const gchar *MODULE_SEARCH_PATTERN = "uca([A-Za-z0-9]+).dll"; +static const gchar *MODULE_SEARCH_PATTERN = "^uca([A-Za-z0-9_-]+)\\.dll$"; static const gchar *MODULE_PRINT_PATTERN = "uca%s.dll"; #else -static const gchar *MODULE_SEARCH_PATTERN = "libuca([A-Za-z0-9]+)"; +static const gchar *MODULE_SEARCH_PATTERN = + "^libuca([A-Za-z0-9_-]+)\\.so(?:\\.[0-9]+)*$"; static const gchar *MODULE_PRINT_PATTERN = "libuca%s.so"; #endif @@ -166,12 +167,16 @@ transform_camera_module_path_to_name (gchar *path, GList **result) { GRegex *pattern; GMatchInfo *match_info; + gchar *basename; pattern = g_regex_new (MODULE_SEARCH_PATTERN, 0, 0, NULL); - g_regex_match (pattern, path, 0, &match_info); + basename = g_path_get_basename (path); + g_regex_match (pattern, basename, 0, &match_info); - *result = g_list_append (*result, g_match_info_fetch (match_info, 1)); + if (g_match_info_matches (match_info)) + *result = g_list_append (*result, g_match_info_fetch (match_info, 1)); g_match_info_free (match_info); + g_free (basename); g_regex_unref (pattern); } @@ -203,7 +208,7 @@ uca_plugin_manager_get_available_cameras (UcaPluginManager *manager) } unsigned int get_num_net_cameras(UcaPluginManager *manager) { - const GList *camera_names = uca_plugin_manager_get_available_cameras(manager); + GList *camera_names = uca_plugin_manager_get_available_cameras(manager); unsigned int count = 0; for (const GList *it = camera_names; it != NULL; it = g_list_next(it)) { @@ -213,6 +218,7 @@ unsigned int get_num_net_cameras(UcaPluginManager *manager) { } } + g_list_free_full (camera_names, g_free); return count; }