diff --git a/Makefile b/Makefile index 8822554..809af5a 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,9 @@ _build/src/opam_publish.%: ALWAYS PREFIX ?= $(shell opam config var prefix) install: - @opam-installer --prefix=$(PREFIX) opam-publish.install + @opam-installer --prefix=$(PREFIX) publish.install remove: - @opam-installer -u --prefix=$(PREFIX) opam-publish.install + @opam-installer -u --prefix=$(PREFIX) publish.install clean: ocamlbuild -clean diff --git a/src/opam_publish.ml b/src/opam_publish.ml index aaadbf0..254320d 100644 --- a/src/opam_publish.ml +++ b/src/opam_publish.ml @@ -612,9 +612,69 @@ let submit ?msg repo_label user_opt package title meta_dir = (* -- Prepare command -- *) +module Pkg = struct + type filename_form = + | Opam + | Pkg_dot_opam of OpamPackage.Name.t + + type t = + { fname_form : filename_form + ; opam_file : OpamFilename.t option + ; descr_file : OpamFilename.t option + ; files_dir : OpamFilename.Dir.t option + } + + let interpret_basename base = + match OpamFilename.Base.to_string base with + | "opam" -> Some Opam + | base when OpamStd.String.ends_with base ~suffix:".opam" -> + Some (Pkg_dot_opam + (OpamStd.String.remove_suffix base ~suffix:".opam" + |> OpamPackage.Name.of_string)) + | _ -> None + + let scan_dir dir = + let open OpamFilename.Op in + let open OpamStd.Option.Op in (* Option monad *) + let opam_and_pkg_dot_opam_files = + OpamStd.List.filter_map + (fun file -> + interpret_basename (OpamFilename.basename file) + >>| fun fname_form -> + match fname_form with + | Opam -> + { fname_form + ; opam_file = Some file + ; descr_file = OpamFilename.opt_file (dir // "descr") + ; files_dir = OpamFilename.opt_dir (dir / "files") + } + | Pkg_dot_opam _ -> + { fname_form + ; opam_file = Some file + ; descr_file = None + ; files_dir = None + }) + (OpamFilename.files dir) + in + let opam_and_pkg_dot_opam_dirs = + OpamStd.List.filter_map + (fun dir -> + interpret_basename (OpamFilename.basename_dir dir) + >>| fun fname_form -> + { fname_form + ; opam_file = OpamFilename.opt_file (dir // "opam" ) + ; descr_file = OpamFilename.opt_file (dir // "descr") + ; files_dir = OpamFilename.opt_dir (dir / "files") + }) + (OpamFilename.dirs dir) + in + opam_and_pkg_dot_opam_files @ opam_and_pkg_dot_opam_dirs +end + let prepare ?name ?version ?(repo_label=default_label) http_url = let open OpamFilename.Op in let open OpamStd.Option.Op in (* Option monad *) + let open Pkg in OpamFilename.with_tmp_dir @@ fun tmpdir -> (* Fetch the archive *) let url = OpamUrl.parse ~handle_suffix:true http_url in @@ -636,90 +696,93 @@ let prepare ?name ?version ?(repo_label=default_label) http_url = (* Utility functions *) let f_opt f = if OpamFilename.exists f then Some f else None in let dir_opt d = if OpamFilename.exists_dir d then Some d else None in - let get_file name reader dir = - dir >>= dir_opt >>= fun d -> - f_opt (d // name) >>= fun f -> + let read_file reader f = try Some (f, reader (OpamFile.make f)) with OpamPp.Bad_format _ -> None in - let get_opam = get_file "opam" OpamFile.OPAM.read in - let get_descr dir = - get_file "descr" OpamFile.Descr.read dir >>= fun (_,d as descr) -> - if OpamFile.Descr.synopsis d = OpamFile.Descr.synopsis descr_template - then None else Some descr - in - let get_files_dir dir = dir >>= dir_opt >>= fun d -> dir_opt (d / "files") in - (* Get opam from the archive *) - let src_meta_dir = dir_opt (srcdir / "opam") ++ dir_opt srcdir in - let src_opam = get_opam src_meta_dir in - (* Guess package name and version *) - let name = match name, src_opam >>| snd >>= OpamFile.OPAM.name_opt with - | None, None -> - OpamConsole.error_and_exit "Package name unspecified" - | Some n1, Some n2 when n1 <> n2 -> - OpamConsole.warning - "Publishing as package %s, while it refers to itself as %s" - (OpamPackage.Name.to_string n1) (OpamPackage.Name.to_string n2); - n1 - | Some n, _ | None, Some n -> n - in - let version = - match version ++ (src_opam >>| snd >>= OpamFile.OPAM.version_opt) with - | None -> - OpamConsole.error_and_exit "Package version unspecified" - | Some v -> v - in - let package = OpamPackage.create name version in - (* Metadata sources: from OPAM overlay, prepare dir, git mirror, archive. - Could add: from highest existing version on the repo ? Better - advise pinning at the moment to encourage some testing. *) - let prepare_dir_name = OpamFilename.cwd () / OpamPackage.to_string package in - let prepare_dir = dir_opt prepare_dir_name in - let overlay_dir = - if has_dotopam <> None then - OpamStateConfig.(!r.current_switch) >>| fun switch -> - OpamPath.Switch.Overlay.package opam_root switch name - else - None - in - let repo = dir_opt (repo_dir repo_label) >>| repo_of_dir in - (repo >>| update_mirror) +! (); - let has_pr = (repo >>| reset_to_existing_pr package) +! false in - let pub_dir = repo >>= get_git_user_dir package in - let other_versions_pub_dir = - if has_pr then None else repo >>= get_git_max_v_dir package - in - (* Choose metadata from the sources *) - let prep_url = - (* Todo: advise mirrors if existing in other versions ? *) - OpamFile.URL.with_checksum [checksum] (OpamFile.URL.create url) + let get_file name dir = + dir >>= dir_opt >>= fun d -> + f_opt (d // name) in - let chosen_opam_and_files = - let get_opam_and_files dir = - get_opam dir >>| fun o -> o, get_files_dir dir + let prepare_one version (pkg : Pkg.t) = + let get_opam dir = get_file "opam" dir >>= read_file OpamFile.OPAM.read in + let read_descr f = + read_file OpamFile.Descr.read f >>= fun (_,d as descr) -> + if OpamFile.Descr.synopsis d = OpamFile.Descr.synopsis descr_template + then None else Some descr in - get_opam_and_files overlay_dir ++ - get_opam_and_files prepare_dir ++ - get_opam_and_files pub_dir ++ - get_opam_and_files src_meta_dir - in - let chosen_descr = - get_descr overlay_dir ++ - get_descr prepare_dir ++ - get_descr pub_dir ++ - get_descr src_meta_dir ++ - get_descr other_versions_pub_dir - in - (* Choose and copy or write *) - OpamFilename.mkdir prepare_dir_name; - let prepare_dir = prepare_dir_name in - match chosen_opam_and_files with - | None -> - OpamConsole.error_and_exit - "No metadata found. \ - Try pinning the package locally (`opam pin add %s %S`) beforehand." - (OpamPackage.Name.to_string name) http_url - | Some ((opam_file, opam), files_opt) -> + let get_descr dir = get_file "descr" dir >>= read_descr in + let get_files_dir dir = dir >>= dir_opt >>= fun d -> dir_opt (d / "files") in + (* Get opam from the archive *) + let src_opam = pkg.opam_file >>= read_file OpamFile.OPAM.read in + (* Guess package name and version *) + let name = match pkg.fname_form, src_opam >>| snd >>= OpamFile.OPAM.name_opt with + | Opam, None -> + OpamConsole.error_and_exit "Package name unspecified" + | Pkg_dot_opam n1, Some n2 when n1 <> n2 -> + OpamConsole.warning + "Publishing as package %s, while it refers to itself as %s" + (OpamPackage.Name.to_string n1) (OpamPackage.Name.to_string n2); + n1 + | Pkg_dot_opam n, _ | Opam, Some n -> n + in + let version = + match version ++ (src_opam >>| snd >>= OpamFile.OPAM.version_opt) with + | None -> + OpamConsole.error_and_exit "Package version unspecified" + | Some v -> v + in + let package = OpamPackage.create name version in + (* Metadata sources: from OPAM overlay, prepare dir, git mirror, archive. + Could add: from highest existing version on the repo ? Better + advise pinning at the moment to encourage some testing. *) + let prepare_dir_name = OpamFilename.cwd () / OpamPackage.to_string package in + let prepare_dir = dir_opt prepare_dir_name in + let overlay_dir = + if has_dotopam <> None then + OpamStateConfig.(!r.current_switch) >>| fun switch -> + OpamPath.Switch.Overlay.package opam_root switch name + else + None + in + let repo = dir_opt (repo_dir repo_label) >>| repo_of_dir in + (repo >>| update_mirror) +! (); + let has_pr = (repo >>| reset_to_existing_pr package) +! false in + let pub_dir = repo >>= get_git_user_dir package in + let other_versions_pub_dir = + if has_pr then None else repo >>= get_git_max_v_dir package + in + (* Choose metadata from the sources *) + let prep_url = + (* Todo: advise mirrors if existing in other versions ? *) + OpamFile.URL.with_checksum [checksum] (OpamFile.URL.create url) + in + let chosen_opam_and_files = + let get_opam_and_files dir = + get_opam dir >>| fun o -> o, get_files_dir dir + in + get_opam_and_files overlay_dir ++ + get_opam_and_files prepare_dir ++ + get_opam_and_files pub_dir ++ + (src_opam >>| fun o -> (o, pkg.files_dir)) + in + let chosen_descr = + get_descr overlay_dir ++ + get_descr prepare_dir ++ + get_descr pub_dir ++ + (pkg.descr_file >>= read_descr) ++ + get_descr other_versions_pub_dir + in + (* Choose and copy or write *) + OpamFilename.mkdir prepare_dir_name; + let prepare_dir = prepare_dir_name in + match chosen_opam_and_files with + | None -> + OpamConsole.error_and_exit + "No metadata found. \ + Try pinning the package locally (`opam pin add %s %S`) beforehand." + (OpamPackage.Name.to_string name) http_url + | Some ((opam_file, opam), files_opt) -> let open OpamFile in let opam = opam |> @@ -754,7 +817,36 @@ let prepare ?name ?version ?(repo_label=default_label) http_url = \ * Run 'opam publish submit ./%s' to submit your package\n" (OpamPackage.to_string package) (OpamPackage.to_string package) - + in + let pkgs = Pkg.scan_dir srcdir in + match List.find (fun pkg -> pkg.fname_form = Opam) pkgs with + | pkg -> + (* If there is an "opam" file or directory, ignore ".opam" files or + directories *) + prepare_one version pkg + | exception Not_found -> + match name with + | None -> + List.iter (prepare_one version) pkgs + | Some name -> + match List.find (fun pkg -> pkg.fname_form = Pkg_dot_opam name) pkgs with + | pkg -> + prepare_one version pkg + | exception Not_found -> + match pkgs with + | [] -> + (* Same as old behavior *) + prepare_one version + { fname_form = Pkg_dot_opam name + ; opam_file = None + ; descr_file = f_opt (srcdir // "descr") + ; files_dir = dir_opt (srcdir / "files") + } + | _ -> + OpamConsole.error_and_exit + "There are .opam files/directories but no %s.opam. \ + I can't decide which opam file to use." + (OpamPackage.Name.to_string name) (* -- Command-line handling -- *)