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

netpbm: new recipe #23402

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions recipes/netpbm/all/conandata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Using versions from a Debian mirror because the upstream only hosts some quite outdated tarballs and an SVN repo with no tagged versions
sources:
"11.6.0":
url: "https://salsa.debian.org/debian-phototools-team/netpbm/-/archive/upstream/11.06.00/netpbm-upstream-11.06.00.tar.bz2"
sha256: "3e0323ff3f472413cef3eeda2a16942eded3ac3117b7d597e3a25f3326208c3d"
238 changes: 238 additions & 0 deletions recipes/netpbm/all/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import os
import shutil

from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.apple import is_apple_os
from conan.tools.build import cross_building
from conan.tools.env import VirtualBuildEnv
from conan.tools.files import get, save, replace_in_file, chdir, rmdir, rm, rename, copy
from conan.tools.gnu import Autotools, AutotoolsToolchain, PkgConfigDeps
from conan.tools.layout import basic_layout


class NetpbmConan(ConanFile):
name = "netpbm"
description = (
"Netpbm is a library for reading and writing Netpbm image formats (PPM, PGM, PBM, PNM) "
"and a toolkit for manipulation of graphic images, including conversion of images between a variety of different formats."
)
license = "IJG AND BSD-3-Clause AND GPL-2.0-only"
url = "https://github.com/conan-io/conan-center-index"
homepage = "http://netpbm.sourceforge.net"
topics = ("image", "graphics", "image-processing", "image-conversion", "ppm", "pgm", "pbm", "pnm")

package_type = "library"
settings = "os", "arch", "compiler", "build_type"
options = {
"shared": [True, False],
"fPIC": [True, False],
"tools": [True, False],
"with_libjpeg": ["libjpeg", "libjpeg-turbo", "mozjpeg"],
"with_x11": [True, False],
}
default_options = {
"shared": False,
"fPIC": True,
"tools": False,
"with_libjpeg": "libjpeg",
"with_x11": True,
}

@property
def _settings_build(self):
return getattr(self, "settings_build", self.settings)

def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
if self.settings.os not in ["Linux", "FreeBSD"]:
del self.options.with_x11

def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
self.settings.rm_safe("compiler.libcxx")
self.settings.rm_safe("compiler.cppstd")

def package_id(self):
if not self.info.options.tools:
self.info.options.rm_safe("with_libjpeg")
self.info.options.rm_safe("with_x11")

def layout(self):
basic_layout(self, src_folder="src")

def requirements(self):
if self.options.tools:
self.requires("jasper/4.2.0")
self.requires("jbig/20160605")
self.requires("libpng/[>=1.6 <2]")
self.requires("libtiff/4.6.0")
self.requires("libxml2/2.12.5")
self.requires("zlib/[>=1.2.11 <2]")
if self.options.with_libjpeg == "libjpeg":
self.requires("libjpeg/9e")
elif self.options.with_libjpeg == "libjpeg-turbo":
self.requires("libjpeg-turbo/3.0.2")
elif self.options.with_libjpeg == "mozjpeg":
self.requires("mozjpeg/4.1.5")
if self.options.get_safe("with_x11"):
self.requires("xorg/system")
# TODO: add ghostscript to CCI

def validate(self):
if self.settings.os == "Windows":
raise ConanInvalidConfiguration("Windows is not supported. Contributions are welcome!")
if cross_building(self):
raise ConanInvalidConfiguration("Cross-building is not supported")

def build_requirements(self):
self.tool_requires("make/4.4.1")
if self.options.tools:
if not self.conf.get("tools.gnu:pkg_config", check_type=str):
self.tool_requires("pkgconf/2.1.0")
self.tool_requires("flex/2.6.4")
self.tool_requires("bison/3.8.2")

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

@property
def _libtype_and_suffix(self):
if self.settings.os == "Windows":
return "dll", "dll"
elif is_apple_os(self):
if self.options.shared:
return "dylib", "dylib"
else:
return "unixstatic", "a"
else:
if self.options.shared:
return "unixshared", "so"
else:
return "unixstatic", "a"

def generate(self):
env = VirtualBuildEnv(self)
env.generate()

tc = AutotoolsToolchain(self)
config = []
lib_type, suffix = self._libtype_and_suffix
config.append(f"NETPBMLIBTYPE={lib_type}")
config.append(f"NETPBMLIBSUFFIX={suffix}")
config.append(f"STATICLIB_TOO={'N' if self.options.shared else 'Y'}")
config.append(f"WANT_SSE={'Y' if self.settings.arch in ['x86', 'x86_64'] else 'N'}")
config.append("DEFAULT_TARGET=nonmerge")
config.append("LDRELOC=ld --reloc")

if is_apple_os(self):
config.append("LDSHLIB=-shared -dynamiclib -install_name @rpath/libnetpbm.dylib")

def _configure_dependency(name, pkg, lib):
cpp_info = self.dependencies[pkg].cpp_info.aggregated_components()
# The file does not need to exist. The build script simply strips 'lib*.a' from the paths.
lib_path = os.path.join(cpp_info.libdir, f"lib{lib}.a")
config.append(f"{name}LIB={lib_path}")
config.append(f"{name}HDR_DIR={cpp_info.includedir}")

def _use_pkgconfig(name):
config.append(f"{name}LIB=USE_PKGCONFIG.a")
config.append(f"{name}HDR_DIR=USE_PKGCONFIG.a")

if self.options.tools:
_configure_dependency("TIFF", "libtiff", "tiff")
_configure_dependency("PNG", "libpng", "png")
_configure_dependency("JPEG", str(self.options.with_libjpeg), "jpeg")
_configure_dependency("JASPER", "jasper", "jasper")
_configure_dependency("JBIG", "jbig", "jbig")
if self.options.get_safe("with_x11"):
_use_pkgconfig("X11")

cflags = list(tc.cflags)
if self.settings.build_type == "Debug":
cflags += ["-g"]
else:
# Match flags set by ./configure
cflags += ["-O3", "-ffast-math", "-fno-finite-math-only"]
if self.options.get_safe("fPIC", True):
cflags.append("-fPIC")
if is_apple_os(self):
# For vasprintf
cflags.append("-D_DARWIN_C_SOURCE")
config.append(f"CFLAGS={' '.join(cflags)}")

tc.make_args.extend(config)
tc.generate()

deps = PkgConfigDeps(self)
deps.generate()

def _patch_sources(self):
# Fix FILE not being defined in jpeglib.h
replace_in_file(self, os.path.join(self.source_folder, "converter", "other", "cameratopam", "camera.c"),
"#include <jpeglib.h>", "#include <stdio.h>\n#include <jpeglib.h>")
Comment on lines +173 to +175
Copy link
Member

Choose a reason for hiding this comment

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

HAs this been reported upstream?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, and the missing #include <stdio.h> appears to be a bug in libjpeg, surprisingly enough. I'll have to investigate it a bit further.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@RubenRBS Indeed, libjpeg itself is buggy. #23428

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope. it's apparently by design for libjpeg. I'll try to submit a patch upstream for Netpbm.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for following up on it, really appreciated! Let me know once this has been reported upstream :)

# add missing -ljpeg to cameratopam
save(self, os.path.join(self.source_folder, "converter", "other", "cameratopam", "Makefile"),
"\ncameratopam: LDFLAGS_TARGET = $(shell $(LIBOPT) $(LIBOPTR) $(JPEGLIB))\n", append=True)

if not self.options.tools:
replace_in_file(self, os.path.join(self.source_folder, "GNUmakefile"),
"PROG_SUBDIRS =", "PROG_SUBDIRS = #")

def build(self):
self._patch_sources()
with chdir(self, self.source_folder):
shutil.copy("config.mk.in", "config.mk")
autotools = Autotools(self)
autotools.make()

def package(self):
for file in ["copyright_summary", "patent_summary", "GPL_LICENSE.txt", "lgpl_v21.txt", "COPYRIGHT.PATENT"]:
copy(self, file, os.path.join(self.source_folder, "doc"), os.path.join(self.package_folder, "licenses"))
temp_dir = self.package_path.joinpath("tmp")
lib_dir = temp_dir.joinpath("lib")
with chdir(self, self.source_folder):
autotools = Autotools(self)
autotools.make(target="package", args=[f"pkgdir={temp_dir}"])
if self.options.shared:
if is_apple_os(self):
soname_file = next(lib_dir.glob("libnetpbm.*.*.dylib"))
rename(self, soname_file, lib_dir.joinpath("libnetpbm.dylib"))
rm(self, "libnetpbm.*.dylib", lib_dir)
AbrilRBS marked this conversation as resolved.
Show resolved Hide resolved
else:
soname_file = next(lib_dir.glob("libnetpbm.so.*")).name
self.run(f"ln -s {soname_file} libnetpbm.so", cwd=lib_dir)
else:
copy(self, "*.a", os.path.join(self.source_folder, "lib"), lib_dir)
rename(self, os.path.join(temp_dir, "include"), os.path.join(self.package_folder, "include"))
rename(self, os.path.join(temp_dir, "lib"), os.path.join(self.package_folder, "lib"))
rename(self, os.path.join(temp_dir, "misc"), os.path.join(self.package_folder, "res"))
if self.options.tools:
rename(self, os.path.join(temp_dir, "bin"), os.path.join(self.package_folder, "bin"))
rmdir(self, temp_dir)

def package_info(self):
self.cpp_info.set_property("pkg_config_name", "netpbm")
self.cpp_info.libs = ["netpbm"]
if self.settings.os in ["Linux", "FreeBSD"]:
self.cpp_info.system_libs.append("m")

if self.options.tools:
self.cpp_info.requires.extend([
"jasper::jasper",
"jbig::jbig",
"libpng::libpng",
"libtiff::libtiff",
"libxml2::libxml2",
"zlib::zlib",
])
if self.options.with_libjpeg == "libjpeg":
self.cpp_info.requires.append("libjpeg::libjpeg")
elif self.options.with_libjpeg == "libjpeg-turbo":
self.cpp_info.requires.append("libjpeg-turbo::jpeg")
elif self.options.with_libjpeg == "mozjpeg":
self.cpp_info.requires.append("mozjpeg::libjpeg")
if self.options.get_safe("with_x11"):
self.cpp_info.requires.append("xorg::x11")
8 changes: 8 additions & 0 deletions recipes/netpbm/all/test_package/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.15)
project(test_package LANGUAGES C)

find_package(netpbm REQUIRED CONFIG)

add_executable(${PROJECT_NAME} test_package.c)
target_link_libraries(${PROJECT_NAME} PRIVATE netpbm::netpbm)
target_compile_features(${PROJECT_NAME} PRIVATE c_std_99)
27 changes: 27 additions & 0 deletions recipes/netpbm/all/test_package/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from conan import ConanFile
from conan.tools.build import can_run
from conan.tools.cmake import cmake_layout, CMake
import os


# It will become the standard on Conan 2.x
class TestPackageConan(ConanFile):
settings = "os", "arch", "compiler", "build_type"
generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv"
test_type = "explicit"

def layout(self):
cmake_layout(self)

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

def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()

def test(self):
if can_run(self):
bin_path = os.path.join(self.cpp.build.bindir, "test_package")
self.run(bin_path, env="conanrun")
38 changes: 38 additions & 0 deletions recipes/netpbm/all/test_package/test_package.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// source: https://netpbm.sourceforge.net/doc/libnetpbm_ug.html

#include <netpbm/pam.h>

void pam_example() {
struct pam inpam, outpam;
tuple * tuplerow;
unsigned int row;

pm_init("image.pam", 0);

pnm_readpaminit(stdin, &inpam, PAM_STRUCT_SIZE(tuple_type));

outpam = inpam;
outpam.file = stdout;

pnm_writepaminit(&outpam);

tuplerow = pnm_allocpamrow(&inpam);

int grand_total = 0;
for (row = 0; row < inpam.height; ++row) {
unsigned int column;
pnm_readpamrow(&inpam, tuplerow);
for (column = 0; column < inpam.width; ++column) {
unsigned int plane;
for (plane = 0; plane < inpam.depth; ++plane) {
grand_total += tuplerow[column][plane];
}
}
pnm_writepamrow(&outpam, tuplerow);
}
pnm_freepamrow(tuplerow);
}

int main() {
return 0;
}
3 changes: 3 additions & 0 deletions recipes/netpbm/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
versions:
"11.6.0":
folder: all
Loading