2017-02-09 3 views
2

Я пытаюсь использовать функцию в качестве значения для ключа :profiles в форме defproject. Начиная от свежего проекта (lein new app test) это работает отлично:Dynamic leiningen: profiles

:profiles {} 

(как вы могли бы надеяться!). Но если бы я изменить его на:

:profiles (merge {}) 

тогда, когда я бегу lein repl она взрывается:

Caused by: java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.util.Map$Entry 

Я смущен этим, так как если я устанавливаю :profiles обратно в пустую карту и попросить Repl эти вещи равны:

test.core=> (= {} (merge {})) 
true 

Где мое недоразумение? Я пропустил что-то основное? Является ли это неудачным артефактом макроса defproject? Что-то другое?

(Clojure 1.8.0, 2.7.1 Leiningen, ява 1.8.0_102)

Edit - рабочий раствор с ответом Скотт:

(def project-name 'myproj) 
(def mains ["foo" "bar"]) 
... 
(defn- lein-alias [main] 
    { main ["with-profile" main] }) 

(defn- lein-profile [main] 
    (let [jar (str main ".jar") 
     entry `~(str project-name "." main)] 
    {(keyword main) {:main entry :bin {:name main} :jar-name jar :uberjar-name jar}})) 

(defproject project-name "0.1.0" 
... 
    :profiles ~(apply merge (concat (map lein-profile mains) {:uberjar {:aot :all}})) 
    :aliases ~(apply merge (map lein-alias mains)) 
    ... 

Так что теперь я могу lein foo bin и lein bar bin к содержанию моего сердца ,

+0

попробовать unquoting это '~ (слияние {})' – Scott

+0

@Scott, что работает. Но почему? –

ответ

3

Если вы unquote Ваша форма Leiningen выполнит форму до оценивается карта проекта.
Итак, ~(merge {}) должен работать.

Существует функция называется unquote-project в Leiningen src/leiningen/core/project.clj#L176

"Внутри defproject формы, unquoting (~) позволяет произвольной оценки."

Похоже, что он выглядит пунктами до unquote, чтобы они могли быть выполнены. Основываясь на комментарий, похоже, это может уйти в 3.0 и предлагает use read-eval syntax


Примечание: Если вы просто пытаетесь объединить значения различных профилей вы должны смотреть на документацию Profiles О Merging и Composite Profiles

Композитные профили

Иногда полезно определить профиль в виде комбинации других профилей. Для этого просто используйте вектор вместо карты в качестве значения профиля. Это может быть использовано, чтобы избежать дублирования:

{:shared {:port 9229, :protocol "https"} 
:qa [:shared {:servers ["qa.mycorp.com"]}] 
:stage [:shared {:servers ["stage.mycorp.com"]}] 
:production [:shared {:servers ["prod1.mycorp.com", "prod1.mycorp.com"]}]} 
+0

Спасибо, это был просто удар, который мне нужен. Я обновляю свой вопрос с помощью своего решения. –

+0

Остерегайтесь этой ошибки, хотя https://github.com/technomancy/leiningen/issues/2132 – lxs

2

Вы не объединяете профили вручную. Они объединяются вместе lein либо автоматически (обычный случай), либо когда вы используете ключевое слово with-profile (ручное управление).

Для примера рассмотрим этот project.clj:

(defproject xyz "0.1.0-SNAPSHOT" 

    :dependencies [ [org.clojure/clojure "1.8.0"] 
        [tupelo "0.9.19"] ] 

    :profiles {:dev  {:dependencies [ [org.clojure/test.check "0.9.0"] 
             [criterium "0.4.4"] ] } 
      :sample {:dependencies [medley "0.8.2"] } 
    ... 
) 

Это project.clj говорит, что проект всегда требует как org.clojure/clojure и tupelo. Во время разработки карта для :dev будет объединена с корневым уровнем, поэтому :dependencies будет обновляться, включая test.check и criterium. Значения профиля :dev не включаются при создании uberjar, поэтому эти библиотеки не будут включены в код, доставляемый пользователям.

Поскольку :sample не один из профилей по умолчанию, он будет включен, только если вы используете команду типа:

> lein with-profile sample test 

Обратите внимание, что ведущий двоеточие не входит в командной строке, хотя мы используем ключевое слово :sample с двоеточием в файле project.clj.

Все подробности здесь: https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md#default-profiles

и здесь: https://github.com/technomancy/leiningen/blob/master/sample.project.clj

Сказав все это, я обычно не нужно использовать :profiles. Если у вас нет чего-то более сложного, чем обычно, вы обычно должны просто положить все свои зависимости на корневом уровне (т.ключевое слово :dependencies на первом уровне под номером (defproject xyz ...) в проекте, который дает lein new app xyz.

Другое предложение: слово test используется во многих местах в проекте lein (имя каталога, суффикс имени файла и т. Д.), Поэтому может быть очень путано назвать сам проект test! Вы сбережете себя (и любых других читателей) некоторое горе, если вы выберете уникальное имя, например xyz, joe или что-нибудь еще.

+0

Спасибо, но я в порядке с концепцией профилей (я думаю!). Мой существующий проект имеет несколько сетей, с несколькими профилями, которые будут использоваться с 'lein-bin'. Поскольку все эти профили очень похожи, я бы хотел использовать функцию для их генерации (DRY). '(merge {})' был всего лишь примером вызова функции, который возвращает карту; '(hash-map)' дает тот же результат. –