Skip to content
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
14 changes: 14 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ jobs:
echo "CPATH=$(brew --prefix libpq)/include:$CPATH" >> "$GITHUB_ENV"
echo "PKG_CONFIG_PATH=$(brew --prefix libpq)/lib/pkgconfig:$PKG_CONFIG_PATH" >> "$GITHUB_ENV"

- name: Set up libpq (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$pgDir = Get-ChildItem "C:\Program Files\PostgreSQL" -Directory |
Sort-Object Name -Descending |
Select-Object -First 1
if (-not $pgDir) { throw "PostgreSQL installation not found" }
$pg = $pgDir.FullName
"PQ_LIB_DIR=$pg\lib" >> $env:GITHUB_ENV
"LIB=$pg\lib;$env:LIB" >> $env:GITHUB_ENV
"INCLUDE=$pg\include;$env:INCLUDE" >> $env:GITHUB_ENV
"PATH=$pg\bin;$env:PATH" >> $env:GITHUB_ENV

- name: Build
run: cargo build --workspace --release --verbose

Expand Down
2 changes: 0 additions & 2 deletions crates/gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ rand = "0.8.5"
base64 = "0.21"
uuid.workspace = true
hyper = "1.4.1"

[target.'cfg(not(target_os = "windows"))'.dependencies]
deadpool-diesel = { version = "0.6.1", features = ["postgres"] }
diesel = { version = "2.2.3", features = ["chrono", "postgres"] }
diesel_migrations = "2"
Expand Down
34 changes: 4 additions & 30 deletions crates/gateway/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
use crate::errors::APIError;
use crate::models::{Request, RequestNewItem, User};

#[cfg(not(target_os = "windows"))]
use crate::schema;
#[cfg(not(target_os = "windows"))]
use crate::{
models::{Request, RequestNewItem, User},
schema,
};
use deadpool_diesel::postgres::{Manager, Pool};
#[cfg(not(target_os = "windows"))]
use diesel::prelude::*;
#[cfg(not(target_os = "windows"))]
use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations};
#[cfg(not(target_os = "windows"))]
use schema::users::dsl::*;

#[cfg(not(target_os = "windows"))]
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/");

#[cfg(not(target_os = "windows"))]
#[derive(Clone)]
pub struct DB {
pool: Pool,
}

#[cfg(target_os = "windows")]
#[derive(Clone)]
pub struct DB;

#[cfg(not(target_os = "windows"))]
impl DB {
pub async fn new(database_url: &str) -> Self {
let manager = Manager::new(database_url, deadpool_diesel::Runtime::Tokio1);
Expand Down Expand Up @@ -77,18 +66,3 @@ impl DB {
}
}
}

#[cfg(target_os = "windows")]
impl DB {
pub async fn new(_database_url: &str) -> Self {
unimplemented!("Postgres-backed DB is not available for Windows targets");
}

pub async fn insert_request(&self, _request: RequestNewItem) -> Result<Request, APIError> {
unimplemented!("Postgres-backed DB is not available for Windows targets");
}

pub async fn authorize_user(&self, _secret_param: String) -> Result<User, APIError> {
unimplemented!("Postgres-backed DB is not available for Windows targets");
}
}
4 changes: 0 additions & 4 deletions crates/gateway/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,12 @@ pub enum APIError {
#[error("Unauthorized registration access")]
Unauthorized(),

#[cfg(not(target_os = "windows"))]
#[error("Database connection error: {0}")]
DatabaseConnection(#[from] deadpool_diesel::PoolError),

#[cfg(not(target_os = "windows"))]
#[error("Database interaction error: {0}")]
DatabaseInteraction(#[from] deadpool_diesel::InteractError),

#[cfg(not(target_os = "windows"))]
#[error("Database query error: {0}")]
DatabaseQuery(#[from] diesel::result::Error),
}
Expand Down Expand Up @@ -81,7 +78,6 @@ impl IntoResponse for APIError {
details: "You are not authorized to access the registration.".to_string(),
},
),
#[cfg(not(target_os = "windows"))]
APIError::DatabaseConnection(_)
| APIError::DatabaseQuery(_)
| APIError::DatabaseInteraction(_) => (
Expand Down
1 change: 0 additions & 1 deletion crates/gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ pub mod errors;
pub mod load_balancer;
pub mod models;
pub mod payload;
#[cfg(not(target_os = "windows"))]
pub mod schema;
1 change: 0 additions & 1 deletion crates/gateway/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ mod errors;
mod load_balancer;
mod models;
mod payload;
#[cfg(not(target_os = "windows"))]
mod schema;

use api::{register, root};
Expand Down
25 changes: 10 additions & 15 deletions crates/gateway/src/models.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};

#[cfg(not(target_os = "windows"))]
use diesel::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Debug)]
#[cfg_attr(not(target_os = "windows"), derive(Queryable, Selectable, Insertable))]
#[cfg_attr(not(target_os = "windows"), diesel(table_name = crate::schema::requests))]
#[cfg_attr(not(target_os = "windows"), diesel(check_for_backend(diesel::pg::Pg)))]
#[derive(Queryable, Selectable, Insertable, Deserialize, Serialize, Debug)]
#[diesel(table_name = crate::schema::requests)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct Request {
pub id: i32,
pub route: String,
Expand All @@ -17,10 +14,9 @@ pub struct Request {
pub reward_address: String,
}

#[derive(Deserialize, Serialize, Debug)]
#[cfg_attr(not(target_os = "windows"), derive(Selectable, Insertable))]
#[cfg_attr(not(target_os = "windows"), diesel(table_name = crate::schema::requests))]
#[cfg_attr(not(target_os = "windows"), diesel(check_for_backend(diesel::pg::Pg)))]
#[derive(Selectable, Insertable, Deserialize, Serialize, Debug)]
#[diesel(table_name = crate::schema::requests)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct RequestNewItem {
pub route: String,
pub user_id: i32,
Expand All @@ -31,10 +27,9 @@ pub struct RequestNewItem {
pub asset_name: Option<String>,
}

#[derive(Deserialize, Serialize, Debug)]
#[cfg_attr(not(target_os = "windows"), derive(Selectable, Insertable, Queryable))]
#[cfg_attr(not(target_os = "windows"), diesel(table_name = crate::schema::users))]
#[cfg_attr(not(target_os = "windows"), diesel(check_for_backend(diesel::pg::Pg)))]
#[derive(Selectable, Insertable, Queryable, Deserialize, Serialize, Debug)]
#[diesel(table_name = crate::schema::users)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct User {
pub id: i32,
pub created_at: NaiveDateTime,
Expand Down
3 changes: 2 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
}
// (lib.optionalAttrs (system == "x86_64-linux") {
blockfrost-platform-x86_64-windows = inputs.self.internal.x86_64-windows.blockfrost-platform;
blockfrost-gateway-x86_64-windows = inputs.self.internal.x86_64-windows.blockfrost-gateway;
});

devshells.default = import ./nix/devshells.nix {inherit inputs;};
Expand Down Expand Up @@ -175,7 +176,7 @@
blockfrost-platform = lib.genAttrs (config.systems ++ crossSystems) (
targetSystem: inputs.self.internal.${targetSystem}.blockfrost-platform
);
blockfrost-gateway = lib.genAttrs config.systems (
blockfrost-gateway = lib.genAttrs (config.systems ++ crossSystems) (
targetSystem: inputs.self.internal.${targetSystem}.blockfrost-gateway
);
devshell = lib.genAttrs config.systems (
Expand Down
137 changes: 137 additions & 0 deletions nix/internal/windows-libpq.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Cross-compile just libpq (PostgreSQL client library) for Windows using MinGW.
#
# We can't use `pkgsCross.mingwW64.postgresql` because the full PostgreSQL
# package is broken for Windows in Nixpkgs. Instead, we build only the client
# library (libpq) from source using Meson cross-compilation.
#
# libpq depends on internal PostgreSQL libraries (pgcommon_shlib, pgport_shlib),
# so we build those too and merge everything into a single static archive.
{
pkgs,
pkgsCross,
}: let
inherit (pkgs) lib;

postgresqlSrc = pkgs.postgresql.src;

crossPrefix = pkgsCross.stdenv.cc.targetPrefix;
crossAr = "${pkgsCross.stdenv.cc}/bin/${crossPrefix}ar";

crossFile = pkgs.writeText "cross-file.txt" ''
[binaries]
c = '${crossPrefix}cc'
cpp = '${crossPrefix}c++'
ar = '${crossPrefix}ar'
strip = '${crossPrefix}strip'
windres = '${crossPrefix}windres'
pkgconfig = 'pkg-config'

[properties]
sys_root = '${pkgsCross.stdenv.cc}/${crossPrefix}'

[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'
'';
in
pkgs.stdenv.mkDerivation {
pname = "libpq-windows";
inherit (pkgs.postgresql) version;

src = postgresqlSrc;

nativeBuildInputs = [
pkgs.meson
pkgs.ninja
pkgs.pkg-config
pkgs.bison
pkgs.flex
pkgs.perl
pkgsCross.stdenv.cc
];

buildInputs = [
pkgsCross.windows.pthreads
];

# We use Meson's cross-compilation support. We only build the `libpq`
# sub-target so we don't need to deal with the full PostgreSQL build.
configurePhase = ''
runHook preConfigure
meson setup build \
--cross-file ${crossFile} \
-Dprefix=$out \
-Ddefault_library=both \
-Dnls=disabled \
-Dreadline=disabled \
-Dzlib=disabled \
-Dssl=none \
-Dgssapi=disabled \
-Dldap=disabled \
-Dpam=disabled \
-Dselinux=disabled \
-Dsystemd=disabled \
-Duuid=none \
-Dicu=disabled \
-Dlz4=disabled \
-Dzstd=disabled \
-Dplperl=disabled \
-Dplpython=disabled \
-Dpltcl=disabled \
-Dllvm=disabled \
-Dlibxml=disabled \
-Dlibxslt=disabled
runHook postConfigure
'';

buildPhase = ''
runHook preBuild
# Build libpq and its internal dependencies (pgcommon, pgport):
ninja -C build \
src/interfaces/libpq/libpq.a \
src/common/libpgcommon.a \
src/common/libpgcommon_shlib.a \
src/port/libpgport.a \
src/port/libpgport_shlib.a
runHook postBuild
'';

installPhase = ''
runHook preInstall
mkdir -p $out/lib $out/include

# Merge all static archives into a single libpq.a so that the linker
# can find all symbols (pgcommon, pgport) that libpq needs:
tmpdir=$(mktemp -d)
for archive in \
build/src/interfaces/libpq/libpq.a \
build/src/common/libpgcommon.a \
build/src/common/libpgcommon_shlib.a \
build/src/port/libpgport.a \
build/src/port/libpgport_shlib.a \
; do
if [ -f "$archive" ]; then
(cd "$tmpdir" && ${crossAr} x "$OLDPWD/$archive")
fi
done
${crossAr} rcs $out/lib/libpq.a "$tmpdir"/*.obj "$tmpdir"/*.o 2>/dev/null \
|| ${crossAr} rcs $out/lib/libpq.a "$tmpdir"/*

# Install headers:
cp src/include/postgres_ext.h $out/include/
cp src/include/pg_config_ext.h $out/include/ 2>/dev/null || true
cp src/interfaces/libpq/libpq-fe.h $out/include/
cp src/interfaces/libpq/libpq-events.h $out/include/
cp build/src/include/pg_config.h $out/include/ 2>/dev/null || true
cp build/src/include/pg_config_ext.h $out/include/ 2>/dev/null || true

runHook postInstall
'';

meta = {
description = "PostgreSQL client library (libpq) cross-compiled for Windows";
license = lib.licenses.postgresql;
};
}
11 changes: 7 additions & 4 deletions nix/internal/windows.nix
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ in rec {

pkgsCross = pkgs.pkgsCross.mingwW64;

# Cross-compile libpq for Windows (pkgsCross.postgresql is broken in Nixpkgs):
libpq-windows = import ./windows-libpq.nix {inherit pkgs pkgsCross;};

packageName = craneLib.crateNameFromCargoToml {cargoToml = src + "/crates/platform/Cargo.toml";};

commonArgs = {
Expand All @@ -43,8 +46,9 @@ in rec {
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include/";

# Unfortunately, `pkgsCross.postgresql` is broken on Windows, so making the
# `blockfrost-gateway` work there will be much more tinkering.
PQ_LIB_DIR = "${libpq-windows}/lib";
PQ_LIB_STATIC = "1";

depsBuildBuild = [
pkgsCross.stdenv.cc
pkgsCross.windows.pthreads
Expand Down Expand Up @@ -81,8 +85,7 @@ in rec {
done
find -name 'build.rs' -delete
'';
}
// (builtins.listToAttrs inputs.self.internal.x86_64-linux.hydraScriptsEnvVars));
});

testgen-hs = let
inherit (inputs.self.internal.x86_64-linux.testgen-hs) version;
Expand Down
Loading