Here's also my conan import implementation, if you want to take a look.

Nicolas
---
 Makefile.am                   |   2 +
 guix/import/conan.scm         | 186 ++++++++++++++++++++++++++++++++++
 guix/scripts/import.scm       |   1 +
 guix/scripts/import/conan.scm | 112 ++++++++++++++++++++
 4 files changed, 301 insertions(+)
 create mode 100644 guix/import/conan.scm
 create mode 100644 guix/scripts/import/conan.scm

diff --git a/Makefile.am b/Makefile.am
index 25ae004fc5..95340989e9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -294,6 +294,7 @@ MODULES =                                   \
   guix/packages.scm                            \
   guix/import/cabal.scm                                \
   guix/import/composer.scm                     \
+  guix/import/conan.scm                                \
   guix/import/cpan.scm                         \
   guix/import/cran.scm                         \
   guix/import/crate.scm                                \
@@ -354,6 +355,7 @@ MODULES =                                   \
   guix/scripts/lint.scm                                \
   guix/scripts/challenge.scm                   \
   guix/scripts/import/composer.scm             \
+  guix/scripts/import/conan.scm                        \
   guix/scripts/import/crate.scm                        \
   guix/scripts/import/cpan.scm                 \
   guix/scripts/import/cran.scm                 \
diff --git a/guix/import/conan.scm b/guix/import/conan.scm
new file mode 100644
index 0000000000..79c620921c
--- /dev/null
+++ b/guix/import/conan.scm
@@ -0,0 +1,186 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Nicolas Graves <ngra...@ngraves.fr>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix import conan)
+  #:use-module (guix http-client)
+  #:use-module (guix import json)
+  #:use-module (guix import utils)
+  #:use-module (guix memoization)
+  #:use-module (guix packages)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 regex)
+  #:use-module (ice-9 textual-ports)
+  #:use-module (json)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-2)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-69)
+  #:export (conan->guix-package
+            guix-package->conan-name))
+
+;;;
+;;; Interface to Conan.io API (center2.conan.io/v2/).
+;;;
+
+(define %conan-base-url
+  "https://center2.conan.io/v2";)
+
+;; Conan package.
+(define-json-mapping <conan-package> make-conan-package conan-package?
+  json->conan-package
+  (name         conan-package-name)                      ;string
+  (version      conan-package-version)                   ;string
+  (user         conan-package-user)                      ;string
+  (channel      conan-package-channel)                   ;string
+  (revision     conan-package-revision)                  ;string
+  (files        conan-package-files))                    ;list of strings
+
+(define (conan-package-reference name version user channel)
+  "Return the complete package reference for the given NAME, VERSION, USER and 
CHANNEL."
+  (string-append name "/" version "@" user "/" channel))
+
+(define (conan-package-reference-split reference)
+  "Split a package reference into its components: name, version, user, 
channel."
+  (match (string-split reference #\@)
+    ((single . ())
+     (append (string-split single #\/) (list "_" "_")))
+    ((first second . ())
+     (append (string-split first #\/) (string-split second #\/)))))
+
+(define (conan-reference-url reference)
+  "Return a base REFERENCE url."
+  (string-join (cons* %conan-base-url "conans"
+                      (conan-package-reference-split reference))
+               "/"))
+
+(define (search-conan-packages name)
+  "Search Conan packages NAME. Returns a list of package references."
+  (and-let* ((url (string-append %conan-base-url "/conans/search?q=" name))
+             (json (json-fetch url)))
+    (vector->list (assoc-ref json "results"))))
+
+(define (get-conan-package-latest-revision reference)
+  "Get the latest revision information for a package REFERENCE."
+  (and-let* ((url (string-append (conan-reference-url reference) "/latest"))
+             (json (json-fetch url)))
+    (assoc-ref json "revision")))
+
+(define (get-conan-package-files reference revision)
+  "Get the list of files associated with a package REFERENCE and REVISION."
+  (and-let* ((url (string-append (conan-reference-url reference)
+                                 "/revisions/" revision "/files"))
+             (json (json-fetch url)))
+    (match (assoc-ref json "files")
+      ((? list? files) (map car files))
+      (_ '()))))
+
+(define (get-conan-package-file reference revision filename)
+  "Get the content of a specific file from a package REFERENCE and REVISION."
+  (let* ((url (string-append (conan-reference-url reference)
+                             "/revisions/" revision "/files/" filename)))
+    (http-fetch url)))
+
+(define (get-conan-package-conanfile reference revision)
+  "Get the conanfile.py content from a package REFERENCE and REVISION."
+  (get-conan-package-file reference revision "conanfile.py"))
+
+(define lookup-conan-package
+  (lambda* (name #:optional (version #f))
+    "Look up NAME and VERSION on Conan.io and return the corresponding package
+reference or #f if it was not found."
+    (and-let* ((references (search-conan-packages name))
+               (reference (if version
+                              (find (match-lambda
+                                      (`(,name ,version ...) #t)
+                                      (_ #f))
+                                    references)
+                              (first references)))
+               (revision (get-conan-package-latest-revision reference)))
+      (apply make-conan-package
+             (append (conan-package-reference-split reference)
+                     (list revision
+                           (get-conan-package-files reference revision)))))))
+
+;;;
+;;; Converting Conan packages to Guix packages.
+;;;
+
+(define (extract-metadata port)
+  "Extract some simple metadata from a conanfile.py content in PORT."
+  (let ((content (get-string-all port)))
+
+    (define (parse target)
+      (and-let* ((found (string-match target content)))
+        (string-trim (match:substring found 1) #\")))
+
+    (list (parse "description\\s*=\\s*\\\"([^\\\"]+)\\\"")
+          (parse "license\\s*=\\s*\\\"([^\\\"]+)\\\"")
+          (parse "homepage\\s*=\\s*\\\"([^\\\"]+)\\\""))))
+
+(define (conan-name->package-name name)
+  "Convert a Conan package name to a Guix package name."
+  (string-downcase name))
+
+(define (guix-package->conan-name package)
+  "Return the Conan name of PACKAGE."
+  (package-name package))
+
+(define* (make-conan-sexp name version #:key source-url
+                          (sha256 (make-string 52 #\0))
+                          license home-page synopsis description)
+  "Return the `package' s-expression for a Conan package with the given NAME,
+VERSION, SOURCE-URL, SHA256, LICENSE, HOME-PAGE, SYNOPSIS, and DESCRIPTION."
+  `(package
+     (name ,(conan-name->package-name name))
+     (version ,version)
+     (source (origin
+               (method git-fetch)
+               (uri (git-reference
+                     (url ,home-page)
+                     (commit (string-append "v" version))))
+               (sha256
+                (base32 ,sha256))))
+     (build-system conan-build-system)
+     (home-page ,home-page)
+     (synopsis ,synopsis)
+     (description ,description)
+     (license ,license)))
+
+(define* (conan->guix-package name #:key version #:allow-other-keys)
+  "Fetch the metadata for the Conan package NAME at VERSION from Conan.io, and
+return the `package' s-expression corresponding to that package, or #f on 
failure."
+  ;; Guile currently doesn't have a native way to parse python files.
+  ;; We can parse (build) requirements with the same trick as in the
+  ;; conan-build-system (evaluating with Python, exporting to
+  ;; text, then reading from Guile), but that would require having python
+  ;; and conan in one's profile.  Not sure this has been done before,
+  ;; skipping this for now.
+  (match (lookup-conan-package name version)
+    (($ <conan-package> name version user channel revision)
+     (and-let* ((reference (conan-package-reference name version user 
channel)))
+       (match (extract-metadata (get-conan-package-conanfile reference 
revision))
+         ((description license homepage . ())
+          (make-conan-sexp name version
+                           #:source-url homepage
+                           #:license (spdx-string->license license)
+                           #:home-page homepage
+                           #:synopsis description
+                           #:description description)))))))
+
+;;; XXX: For the updater, we have information about versions, but not about
+;;; source, or probably not better than the built-in github updater.
diff --git a/guix/scripts/import.scm b/guix/scripts/import.scm
index bbf31baa15..4d0fd689a5 100644
--- a/guix/scripts/import.scm
+++ b/guix/scripts/import.scm
@@ -50,6 +50,7 @@ (define %standard-import-options '())
 ;; The list of all known importers.  These are printed in order by SHOW-HELP, 
so
 ;; please keep this list alphabetically sorted!
 (define importers '("composer" "cpan" "cran" "crate" "egg" "elm" "elpa"
+                    "conan"
                     "gem" "gnu" "go" "hackage" "hexpm" "json" "minetest"
                     "npm-binary" "opam" "pypi" "stackage" "texlive"))
 
diff --git a/guix/scripts/import/conan.scm b/guix/scripts/import/conan.scm
new file mode 100644
index 0000000000..0b6746f255
--- /dev/null
+++ b/guix/scripts/import/conan.scm
@@ -0,0 +1,112 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Nicolas Graves <ngra...@ngraves.fr>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts import conan)
+  #:use-module (guix ui)
+  #:use-module (guix utils)
+  #:use-module (guix scripts)
+  #:use-module (guix import conan)
+  #:use-module (guix scripts import)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-37)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 format)
+  #:export (guix-import-conan))
+
+;;;
+;;; Command-line options.
+;;;
+(define %default-options
+  '())
+
+(define (show-help)
+  (display (G_ "Usage: guix import conan PACKAGE-NAME
+Import and convert the Conan.io package for PACKAGE-NAME.\n"))
+  (display (G_ "
+  -r, --recursive        import packages recursively"))
+  (display (G_ "
+  -u, --user=USER        specify the package user"))
+  (display (G_ "
+  -c, --channel=CHANNEL  specify the package channel"))
+  (newline)
+  (display (G_ "
+  -h, --help             display this help and exit"))
+  (display (G_ "
+  -V, --version          display version information and exit"))
+  (newline)
+  (show-bug-report-information))
+
+(define %options
+  ;; Specification of the command-line options.
+  (cons* (option '(#\h "help") #f #f
+                 (lambda args
+                   (show-help)
+                   (exit 0)))
+         (option '(#\V "version") #f #f
+                 (lambda args
+                   (show-version-and-exit "guix import conan")))
+         (option '(#\r "recursive") #f #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'recursive #t result)))
+         (option '(#\u "user") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'user arg result)))
+         (option '(#\c "channel") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'channel arg result)))
+         %standard-import-options))
+
+;;;
+;;; Entry point.
+;;;
+(define (guix-import-conan . args)
+  (define (parse-options)
+    ;; Return the alist of option values.
+    (parse-command-line args %options (list %default-options)
+                        #:build-options? #f))
+
+  (let* ((opts (parse-options))
+         (args (filter-map (match-lambda
+                             (('argument . value)
+                              value)
+                             (_ #f))
+                           (reverse opts))))
+    (match args
+      ((spec)
+       (define-values (name version)
+         (package-name->name+version spec))
+       (let ((version (or version (assoc-ref opts 'version))))
+         (match (if (assoc-ref opts 'recursive)
+                    (conan-recursive-import
+                     name #:version version)
+                    (conan->guix-package
+                     name #:version version
+                     #:user (assoc-ref opts 'user)
+                     #:channel (assoc-ref opts 'channel)))
+           ((or #f '())
+            (leave (G_ "failed to download meta-data for package '~a'~%")
+                   (if version
+                       (string-append name "@" version)
+                       name)))
+           ((? list? sexps) sexps)
+           (sexp (list sexp)))))
+      (()
+       (leave (G_ "too few arguments~%")))
+      ((many ...)
+       (leave (G_ "too many arguments~%"))))))
-- 
2.49.0



-- 
Best regards,
Nicolas Graves

Reply via email to