Skip to content
This repository was archived by the owner on Oct 1, 2020. It is now read-only.

use pkg-config by default, fall back to source build automatically #25

Merged
merged 7 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
41 changes: 22 additions & 19 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,40 @@ rust:
os:
- linux
- osx
# by default, test vanilla setup on all three channels
env:
global:
- PROJ_FEATURES=""
- INSTALL_PROJ_FIRST=1 _PROJ_SYS_TEST_EXPECT_BUILD_FROM_SRC=0

# test both features on Linux stable, but only pkg_config on macOS stable
jobs:
include:
- os: linux
env: PROJ_FEATURES="--features bundled_proj"
- os: linux
env: PROJ_FEATURES="--features pkg_config"
env: INSTALL_PROJ_FIRST=0 _PROJ_SYS_TEST_EXPECT_BUILD_FROM_SRC=1
- os: osx
env: PROJ_FEATURES="--features pkg_config"
env: INSTALL_PROJ_FIRST=0 _PROJ_SYS_TEST_EXPECT_BUILD_FROM_SRC=1
# verify we can build from src even if its already installed
- os: linux
env: INSTALL_PROJ_FIRST=1 PROJ_FEATURES="--features bundled_proj" _PROJ_SYS_TEST_EXPECT_BUILD_FROM_SRC=1
- os: osx
env: PROJ_FEATURES="--features bundled_proj"
env: INSTALL_PROJ_FIRST=1 PROJ_FEATURES="--features bundled_proj" _PROJ_SYS_TEST_EXPECT_BUILD_FROM_SRC=1

before_install:
- |
if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$PROJ_FEATURES" != "--features bundled_proj" ]; then
sudo apt-get update
sudo apt-get -y install pkg-config
wget https://download.osgeo.org/proj/proj-7.1.0.tar.gz
tar -xzvf proj-7.1.0.tar.gz
pushd proj-7.1.0 && ./configure --prefix=/usr && make && sudo make install && popd
fi
- |
if [ "$TRAVIS_OS_NAME" == "osx" ] && [ "$PROJ_FEATURES" != "--features bundled_proj" ]; then
brew update
brew upgrade pkg-config
brew upgrade proj
if [[ $INSTALL_PROJ_FIRST == "1" ]]; then
case $TRAVIS_OS_NAME in
"linux")
sudo apt-get update
sudo apt-get -y install pkg-config
wget https://download.osgeo.org/proj/proj-7.1.0.tar.gz
tar -xzvf proj-7.1.0.tar.gz
pushd proj-7.1.0 && ./configure --prefix=/usr && make && sudo make install && popd
;;
"osx")
brew update
brew upgrade pkg-config
brew upgrade proj
;;
esac
fi

script:
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ tar = "0.4.26"

[features]
nobuild = []
pkg_config = []
bundled_proj = []
# `pkg_config` feature is deprecated and does nothing
pkg_config = []

[package.metadata.docs.rs]
features = [ "nobuild" ] # This feature will be enabled during the docs.rs build
32 changes: 18 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
[![Build Status](https://travis-ci.org/georust/proj-sys.svg?branch=master)](https://travis-ci.org/georust/proj-sys)

# Low-level bindings for PROJ v7.1.x
**This is a [`*-sys`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#a-sys-packages) crate; you shouldn't use its API directly.** The [`proj`](https://github.com/georust/proj) crate is designed for general use.

A guide to the functions can be found here: https://proj.org/development/reference/functions.html. Run `cargo doc (optionally --open)` to generate the crate documentation.
**This is a
[`*-sys`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages)
crate; you shouldn't use its API directly.** See the
[`proj`](https://github.com/georust/proj) crate for general use.

## Requirements
A guide to the functions can be found here:
https://proj.org/development/reference/functions.html.

By default, `libproj` (via `PROJ v7.1.x`) must be present on your system. While this crate may be backwards-compatible with older PROJ 7 and PROJ 6 versions, this is neither tested or supported.
By default, the crate will search for an existing `libproj` (via `PROJ v7.1.x`)
installation on your system using
[pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/).

## Optional Features
Enable these in your `Cargo.toml` like so:
If an acceptable installation is not found, proj-sys will attempt to build
libproj from source bundled in the crate.

`proj-sys = { version = "0.18.1", features = ["bundled_proj"] }`
`proj-sys = { version = "0.18.1", features = ["pkg_config"] }`
## Features

Note that these features are **mutually exclusive**.

1. `bundled_proj` (Linux and macOS targets):
- allow the crate to internally build and depend on a bundled `libproj`. Note that SQLite3 and `libtiff` must be present on your system if you wish to use this feature, and that it builds `libproj` **without** its native network functionality; you will have to implement your own set of callbacks if you wish to make use of them (see the [`proj`](https://crates.io/crates/proj) crate for an example).
2. `pkg_config` (Linux and macOS targets)
- uses [`pkg-config`](https://en.wikipedia.org/wiki/Pkg-config) to add search paths to the build script. Requires `pkg-config` to be installed (available on Homebrew, Macports, apt etc.)
`bundled_proj` - forces building libproj from source even if an acceptable
version could be found on your system. Note that SQLite3 and `libtiff` must be
present on your system if you wish to use this feature, and that it builds
`libproj` **without** its native network functionality; you will have to
implement your own set of callbacks if you wish to make use of them (see the
[`proj`](https://crates.io/crates/proj) crate for an example).

## License

Expand Down
139 changes: 43 additions & 96 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,103 +1,53 @@
use bindgen;
#[cfg(all(
not(feature = "pkg_config"),
feature = "bundled_proj",
not(feature = "nobuild")
))]
use cmake;
#[cfg(all(
not(feature = "pkg_config"),
feature = "bundled_proj",
not(feature = "nobuild")
))]
use flate2::read::GzDecoder;
#[cfg(all(
not(feature = "pkg_config"),
feature = "bundled_proj",
not(feature = "nobuild")
))]
use std::fs::File;

#[cfg(all(
feature = "pkg_config",
not(feature = "bundled_proj"),
not(feature = "nobuild")
))]
use pkg_config;
use std::env;
use std::path::PathBuf;
#[cfg(all(
not(feature = "pkg_config"),
feature = "bundled_proj",
not(feature = "nobuild")
))]
use tar::Archive;

#[cfg(all(
feature = "pkg_config",
not(feature = "bundled_proj"),
not(feature = "nobuild")
))]
const MINIMUM_PROJ_VERSION: &str = "7.1.0";

#[cfg(feature = "nobuild")]
fn main() {} // Skip the build script on docs.rs

// We sometimes need additional search paths, which we get using pkg-config
#[cfg(all(
feature = "pkg_config",
not(feature = "nobuild"),
not(feature = "bundled_proj")
))]
fn main() {
let pk = pkg_config::Config::new()
#[cfg(not(feature = "nobuild"))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let include_path = if cfg!(feature = "bundled_proj") {
eprintln!("feature flags specified source build");
build_from_source()?
} else {
pkg_config::Config::new()
.atleast_version(MINIMUM_PROJ_VERSION)
.probe("proj")
.expect(&format!(
"Your PROJ version may be too old. You need at least version {}",
MINIMUM_PROJ_VERSION
));
// Tell cargo to tell rustc to link the system proj
// shared library.
println!("cargo:rustc-link-search=native={:?}", pk.link_paths[0]);
println!("cargo:rustc-link-lib=proj");
let include_path = pk.include_paths[0].to_string_lossy();

// The bindgen::Builder is the main entry point
// to bindgen, and lets you build up options for
// the resulting bindings.
let bindings = bindgen::Builder::default()
.clang_arg(format!("-I{}", include_path))
.trust_clang_mangling(false)
.blacklist_type("max_align_t")
// The input header we would like to generate
// bindings for.
.header("wrapper.h")
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");
.and_then(|pk| {
eprintln!("found acceptable libproj already installed at: {:?}", pk.link_paths[0]);
if let Ok(val) = &env::var("_PROJ_SYS_TEST_EXPECT_BUILD_FROM_SRC") {
if val != "0" {
panic!("for testing purposes: existing package was found, but should not have been");
}
}

// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}
// Tell cargo to tell rustc to link the system proj
// shared library.
println!("cargo:rustc-link-search=native={:?}", pk.link_paths[0]);
println!("cargo:rustc-link-lib=proj");

// Vanilla
#[cfg(all(
not(feature = "pkg_config"),
not(feature = "nobuild"),
not(feature = "bundled_proj")
))]
fn main() {
println!("cargo:rustc-link-lib=proj");
Ok(pk.include_paths[0].clone())
})
.or_else(|err| {
eprintln!("pkg-config unable to find existing libproj installation: {}", err);
build_from_source()
})?
};

// The bindgen::Builder is the main entry point
// to bindgen, and lets you build up options for
// the resulting bindings.
let bindings = bindgen::Builder::default()
.clang_arg(format!("-I{}", include_path.to_string_lossy()))
.trust_clang_mangling(false)
.blacklist_type("max_align_t")
// The input header we would like to generate
Expand All @@ -110,25 +60,29 @@ fn main() {

// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
bindings.write_to_file(out_path.join("bindings.rs"))?;

Ok(())
}

#[cfg(all(
not(feature = "pkg-config"),
not(feature = "nobuild"),
feature = "bundled_proj"
))]
fn main() {
// Build PROJ from the included tar
// returns the path of "inlude" for the built proj
fn build_from_source() -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
eprintln!("building libproj from source");
if let Ok(val) = &env::var("_PROJ_SYS_TEST_EXPECT_BUILD_FROM_SRC") {
if val == "0" {
panic!(
"for testing purposes: package was building from source but should not have been"
);
}
}

// NOTE: The PROJ build expects Sqlite3 to be present on the system.
let path = "PROJSRC/proj-7.1.0.tar.gz";
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let tar_gz = File::open(path).expect("Couldn't open PROJ source tar");
let tar_gz = File::open(path)?;
let tar = GzDecoder::new(tar_gz);
let mut archive = Archive::new(tar);
archive.unpack("PROJSRC/proj").expect("Couldn't unpack tar");
archive.unpack("PROJSRC/proj")?;
let mut config = cmake::Config::new("PROJSRC/proj/proj-7.1.0");
config.define("BUILD_SHARED_LIBS", "OFF");
config.define("BUILD_TESTING", "OFF");
Expand Down Expand Up @@ -168,12 +122,5 @@ fn main() {
println!("cargo:warning=proj-sys: Not configuring an explicit C++ standard library on this target.");
}

bindgen::builder()
.header(proj.join("include").join("proj.h").to_str().unwrap())
.trust_clang_mangling(false)
.blacklist_type("max_align_t")
.generate()
.expect("Unable to generate bindings")
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
Ok(proj.join("include"))
}
54 changes: 24 additions & 30 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,30 @@
#![allow(non_snake_case)]
#![doc(html_logo_url = "https://raw.githubusercontent.com/georust/meta/master/logo/logo.png")]
//! # Low-level bindings for PROJ v7.1.x
//! **This is a [`*-sys`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#a-sys-packages) crate; you shouldn't use its API directly.** The [`proj`](https://github.com/georust/proj) crate is designed for general use.
//!
//! A guide to the functions can be found on [proj.org](https://proj.org/development/reference/functions.html). Run `cargo doc (optionally --open)` to generate the crate documentation.
//!
//! ## Requirements
//!
//! By default, `libproj` (via `PROJ v7.1.x`) must be present on your system. While this crate may be backwards-compatible with older PROJ 7 and PROJ 6 versions, this is neither tested or supported.
//!
//! ## Optional Features
//! Enable these in your `Cargo.toml` like so:
//!
//! `proj-sys = { version = "0.18.2", features = ["bundled_proj"] }`
//! `proj-sys = { version = "0.18.2", features = ["pkg_config"] }`
//!
//! Note that these features are **mutually exclusive**.
//!
//! 1. `bundled_proj` (Linux and macOS targets):
//! - allow the crate to internally build and depend on a bundled `libproj`. Note that SQLite3 and `libtiff` must be present on your system if you wish to use this feature, and that it builds `libproj` **without** its native network functionality; you will have to implement your own set of callbacks if you wish to make use of them (see the [`proj`](https://crates.io/crates/proj) crate for an example).
//! 2. `pkg_config` (Linux and macOS targets)
//! - uses [`pkg-config`](https://en.wikipedia.org/wiki/Pkg-config) to add search paths to the build script. Requires `pkg-config` to be installed (available on Homebrew, Macports, apt etc.)
//!
//! ## License
//!
//! Licensed under either of
//!
//! * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
//! * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
//!
//! at your option.

//!
//! **This is a
//! [`*-sys`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages)
//! crate; you shouldn't use its API directly.** See the
//! [`proj`](https://github.com/georust/proj) crate for general use.
//!
//! A guide to the functions can be found here:
//! https://proj.org/development/reference/functions.html.
//!
//! By default, the crate will search for an existing `libproj` (via `PROJ v7.1.x`)
//! installation on your system using
//! [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/).
//!
//! If an acceptable installation is not found, proj-sys will attempt to build
//! libproj from source bundled in the crate.
//!
//! ## Features
//!
//! `bundled_proj` - forces building libproj from source even if an acceptable
//! version could be found on your system. Note that SQLite3 and `libtiff` must be
//! present on your system if you wish to use this feature, and that it builds
//! `libproj` **without** its native network functionality; you will have to
//! implement your own set of callbacks if you wish to make use of them (see the
//! [`proj`](https://crates.io/crates/proj) crate for an example).

#[cfg(not(feature = "nobuild"))]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
Expand Down