Построить рубиновый камень и условно определить зависимости
Я работаю над самоцветом, которому необходимо установить зависимости условно, когда камень установлен. Я проделал кое-какие операции вокруг
и похоже, что я не одинок в этой необходимости.
Rubygems: как добавить зависимую от платформы зависимость?
это длинный поток
http://www.ruby-forum.com/topic/957999
Единственный способ, с помощью которого я могу добавить зависимости к gem, - это использовать метод add_dependency в блоке Gem:: Specifiction в файле .gemspec
Gem::Specification.new do |s|
# ... standard setup stuff
# conditionally set dependencies
s.add_dependency "rb-inotify", "~> 0.8.8" if RUBY_PLATFORM =~ /linux/i
s.add_dependency "rb-fsevent", "~> 0.4.3.1" if RUBY_PLATFORM =~ /darwin/i
s.add_dependency "rb-fchange", "~> 0.0.5" if RUBY_PLATFORM =~ /mswin|mingw/i
end
Основываясь на всех документах и потоках, найденных в сети, я ожидал, что если вы установите драгоценный камень на
- Linux, тогда rb-inotify будет зависимым и автоматически установленным
- Mac-rb-fsevent будет установлен
- Windows-rb-fchange будет установлен
Однако, похоже, это не так. Операторы "if" внутри блока оцениваются во время создания и упаковки драгоценного камня. Следовательно,
если вы создаете и упаковываете драгоценный камень в Linux, тогда rb-inotify добавляется как зависимость, Mac, затем rb-fsevent, Windows-rb-fchange.
Все еще нуждаясь в решении, я выкопался в коде rubygems, и, похоже, следующее - это широкий ответ о том, что происходит.
- постройте весь свой код для своего драгоценного камня: foo.gem
- создать файл foo.gemspec
- построить, пакет и выпустить драгоценный камень на сервер драгоценных камней, например rubygems.org
- пусть все знают
- разработчики устанавливают его локально через: gem install foo
- файл foo.gem загружается, распаковывается и устанавливается. все зависимости также установлены.
- все должно быть установлено, и мы можем попросить использовать драгоценный камень.
Похоже, что когда камень построен и выпущен, файл foo.gemspec загружается и блок Gem:: Specification вычисляется и преобразуется в YAML, сжатый как
metadata.gz и включен в foo.gem. Код ruby сжимается в data.tar.gz и включается также. Когда камень установлен на локальной машине разработчика,
YAML извлекается из metadata.gz и преобразуется обратно в блок Gem:: Specification, однако он не преобразовывается обратно в исходный блок.
вместо этого вы увидите что-то вроде следующего:
Gem::Specification.new do |s|
if s.respond_to? :specification_version then
s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<rb-inotify>, ["~> 0.8.8"])
else
s.add_dependency(%q<rb-inotify>, ["~> 0.8.8"])
end
else
s.add_dependency(%q<rb-inotify>, ["~> 0.8.8"])
end
end
Ok. Тем не менее, у меня есть взгляд на птицу, но это не меняет моего желания построить один драгоценный камень и условно определять зависимости для целого ряда целей ОС.
Если у кого-то есть решение, отличное от создания нескольких файлов .gemspec для каждой целевой ОС... Я все уши!
Ответы
Ответ 1
Я никогда не делал этого сам, но есть некоторые драгоценные камни, которые доступны в конкретных версиях платформы: http://rubygems.org/gems/libv8/versions
от того, что я понимаю, это всего лишь именование, которое можно настроить, установив платформенный вариант вашего gemspec. взгляните на документ: http://guides.rubygems.org/specification-reference/#platform=
Ответ 2
Я также наткнулся на эту проблему в прошлом. Единственным обходным решением, которое я смог найти, было создание задачи Rake для установки зависимостей. Конечно, на этом этапе вы можете просто хотеть, чтобы пользователь сам определил, какой жемчуг он отсутствует на основании сообщения об ошибке, которое он получает. В моем случае было установлено несколько зависимых от платформы зависимостей, поэтому это не вариант.
Rakefile:
task :install do |t|
require './lib/library/installer'
Library::Installer.install
end
Установщик:
module Library::Installer
require 'rubygems/dependency_installer'
def self.install
installer = Gem::DependencyInstaller.new
dependency = case RUBY_PLATFORM
when /darwin/i then ["rb-fsevent", "~> 0.4.3.1"]
when /linux/i then ["rb-inotify", "~> 0.8.8"]
when /mswin|mingw/i then ["rb-fchange", "~> 0.0.5"]
end
installer.install(*dependency)
end
Затем пользователь может использовать rake install
для установки соответствующих зависимостей.
Условная установка зависимостей (не только основанная на платформе, но и основанная на пользовательском вводе, например) жестоко отсутствует для RubyGems. Пусть надеется, что это будет реализовано в будущем!
Ответ 3
Я также изучил это и пришел к выводу, что это невозможно по дизайну. Наличие единого "мега-жемчужина" для всех платформ вызывает проблему незнания поддержки платформы, пока не будет загружена и установлена копия. Драгоценный камень должен был быть достаточно умным, чтобы определить, какой правильный способ установить в зависимости от платформы. Если платформа вообще не поддерживается, жемчужина может сильно потерпеть неудачу, открыв большую банку червей. Там используется обратный вызов после того, как был установлен камень, который был удален по той же причине, без магии, чтобы правильно установить жемчужину. Некоторые люди взломали это, используя mkmf, но я предлагаю использовать более дорогой путь драгоценного камня на платформу.
Основываясь на этом, в проекте, который создает драгоценный камень для ruby и jruby, я должен вручную создать каждый камень и загрузить их в RubyGem. Используя Jeweler, это так же просто, как указано Gemfile, но мне нужно перестроить спецификацию gem каждый раз, когда я упаковываю драгоценный камень. Довольно тривиально, когда поддерживаем только 2 платформы, но процесс сборки достаточно прост, чтобы его можно было автоматизировать, чтобы обеспечить поддержку многоплатформенных камней.