Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Makefile değil, Rakefile

Makefile değil, Rakefile

Efsanevi `ruby` build tool'u

Uğur Özyılmazel

October 05, 2019
Tweet

More Decks by Uğur Özyılmazel

Other Decks in Programming

Transcript

  1. UĞUR “vigo” ÖZYILMAZEL
    Makefile değil,
    Rakefile
    vigo
    5 Ekim 2019, 13:00

    View Slide

  2. `make` nedir?
    Uygulamaları çalıştırmaya ve derlemeye
    yarayan kullanışlı otomasyon aracıdır.
    https://opensource.com/article/18/8/what-how-makefile

    View Slide

  3. `Makefile` nedir?
    `make` uygulamasının işini yapabilmesi için
    gereken işlemlerin akışının saklandığı dosya,
    yani bir tür reçetedir.
    https://opensource.com/article/18/8/what-how-makefile

    View Slide

  4. `Makefile` neye benzer ?
    https://opensource.com/article/18/8/what-how-makefile
    # Makefile
    say_hello:
    echo "Hello World"
    $ make
    $ echo "Hello World"
    $ Hello World

    View Slide

  5. `Makefile` faydaları ?
    Bir kısım kural içerir, bu kurallar birbirine bağlı
    olabilirler ya da sırayla birbirini takip edebilirler.
    https://opensource.com/article/18/8/what-how-makefile

    View Slide

  6. `Makefile` prensibi
    http://www.wikiwand.com/en/Makefile
    hedef: bağımlılıklar
    sistem komutları/komutlar

    View Slide

  7. `Makefile` örneği
    https://github.com/azer/go-makefile-example/blob/master/Makefile
    https://kodfabrik.com/journal/a-good-makefile-for-go/
    start:
    @bash -c "trap 'make stop' EXIT; $(MAKE) clean compile start-server watch run='make clean compile start-server'"
    ## stop: Stop development mode.
    stop: stop-server
    start-server: stop-server
    @echo " > $(PROJECTNAME) is available at $(ADDR)"
    @-$(GOBIN)/$(PROJECTNAME) 2>&1 & echo $$! > $(PID)
    @cat $(PID) | sed "/^/s/^/ \> PID: /"
    stop-server:
    @-touch $(PID)
    @-kill `cat $(PID)` 2> /dev/null || true
    @-rm $(PID)
    ## watch: Run given command when code changes. e.g; make watch run="echo 'hey'"
    watch:
    @GOPATH=$(GOPATH) GOBIN=$(GOBIN) yolo -i . -e vendor -e bin -c "$(run)"
    restart-server: stop-server start-server
    ## compile: Compile the binary.
    compile:
    @-touch $(STDERR)
    @-rm $(STDERR)
    @-$(MAKE) -s go-compile 2> $(STDERR)
    @cat $(STDERR) | sed -e '1s/. */\nError:\n/' | sed 's/make\[. */ /' | sed "/^/s/^/ /" 1>&2

    View Slide

  8. `Makefile` ve `make` özetle...
    • bash diline benziyor
    • unix türevleri ve windows’da çalışıyor (cygwin, mingw)
    • karmaşıklık artıkça anlaşılırlık da azalıyor
    • orta zorluk seviyesinde
    https://opensource.com/article/18/8/what-how-makefile

    View Slide

  9. `rake` nedir?
    Rake is a build language,
    similar in purpose to make and
    ant.
    Martin Fowler

    View Slide

  10. `rake` nedir?
    • `make`’in Ruby’cesidir.
    • Ruby kurulumunda beraber gelir, bir gem’dir.
    • Komut satırı ortamında her projede kullanılabilir.
    • Domain Specific Language (DSL) için en iyi örnektir.
    • En güncel sürümü için `gem install rake` yeterlidir.

    View Slide

  11. `rake` in babası kimdir ?
    https://www.wikiwand.com/en/Jim_Weirich
    • Jim Nolan Weirich tarafından
    geliştirildi. (1956-2014)
    • macOS 10.5 (Leopard)’dan beri
    birlikte geliyor (2006).
    • https://l.vigo.io/rake-10-5

    View Slide

  12. Diğer build tool’lardan farkı ?
    • `make` kendine mahsus farklı DSL’i var
    • `ant` XML tabanlı farklı bir DSL’i var
    • `rake` Ruby DSL’i var :)

    View Slide

  13. Neden tercih ediyorum ?
    • Bash hız açısından iyi ama limitli
    • macOS ile birlikte `ruby` geldiği için `rake` de
    built-in, ek bir araca ihtiyaç yok!
    • Local (folder bazında) ya da Global (tüm sistem
    bazında) çalışan Rakefile tanımlamak mümkün
    • Çünkü ruby ❤

    View Slide

  14. scope
    • /path/to/project/Rakefile # local, `rake`
    • $HOME/.rake/file.rake # global `rake -g TASK`

    View Slide

  15. projenin herhangi bir yerinden!

    View Slide

  16. basit bir task
    desc "Merhaba Ruby Türkiye"
    task :hello do
    puts "Merhaba Rubyiciler!"
    end
    $ rake -T
    $ rake —tasks
    $ rake hello # Merhaba Ruby Türkiye

    View Slide

  17. default task
    $ rake
    rake aborted!
    Don't know how to build task 'default' (See the list of
    available tasks with `rake --tasks`)
    (See full trace by running task with --trace)

    View Slide

  18. default task
    desc "Merhaba Ruby Türkiye"
    task :hello do
    puts "Merhaba Rubyiciler!"
    end
    desc "Bu varsayılan task'i çalıştırır: :hello"
    task :default => [:hello]
    $ rake —tasks
    rake default # Bu varsayılan task'i çalıştırır: :hello
    rake hello # Merhaba Ruby Türkiye

    View Slide

  19. Task (görev/iş) türleri
    • Basit task’ler
    • Önşart’lı task’ler / zincileme task’ler
    • File, Directory task’leri
    • Paralel çalışan task’ler
    • Rule bazlı task’ler

    View Slide

  20. Çoklu çağırma
    desc ":hello task"
    task :hello do
    print "hello "
    end
    desc ":world task"
    task :world do
    print "world "
    end
    $ rake —T
    rake hello # :hello task
    rake world # :world task
    $ rake hello world # hello world

    View Slide

  21. Önşartlı task / zincirleme
    task :check_env do
    abort "environment variable FOO is not set" unless ENV['FOO']
    end
    desc "say hello world"
    task :hello_world => [:check_env] do
    puts "hello world with FOO: #{ENV['FOO']}"
    end
    $ rake -T
    rake hello_world # say hello world
    $ rake hello_world
    environment variable FOO is not set
    $ FOO=1 rake hello_world
    hello world with FOO: 1

    View Slide

  22. File task’leri
    file "/path/to/output" => "/path/to/input" do
    # your code here
    end
    file "/path/to/output" => ["/path/to/input1", "/path/to/input2"] do |t|
    # your code here
    end
    file "merged.txt" => ["file1", "file2"] do |t|
    sh "cat #{t.prerequisites.join(' ')} > #{t.name}"
    # cat file1 file2 > merged.txt
    end
    $ rake merged.txt

    View Slide

  23. rake task’leri ne zaman çalışmaz ?
    • `merged.txt`dosyası fiziksel
    olarak var ise
    • `file1` ve `file2` dosyaları
    değişmemişse

    View Slide

  24. File task’leri
    %W[doc-1.md doc-2.md doc-3.md].each do |md_file|
    html_file = File.basename(md_file, ".md") + ".html"
    file html_file => md_file do
    puts "convert markdown: #{md_file} to html: #{html_file}"
    touch html_file
    end
    end
    $ touch doc-{1,2,3}.md # fake dosyaları oluşturduk
    $ rake doc-1.html
    convert markdown: doc-1.md to html: doc-1.html
    touch doc-1.html
    $ rake doc-1.html # tekrar çağırdık, çalışmadı, çünkü değişen dosya yok!
    http://www.virtuouscode.com/2014/04/21/rake-part-1-basics/

    View Slide

  25. Directory task’leri
    directory "merged_files"
    file "merged.txt" => "merged_files" do |t|
    puts "create folder called: #{t.prerequisites.first}/ unless it exists"
    puts "name of task: #{t.name}"
    puts "prerequisite(s) of task: #{t.prerequisites.first}"
    end
    $ rake merged.txt
    mkdir -p merged_files
    create folder called: merged_files/ unless it exists
    name of task: merged.txt
    prerequisite(s) of task: merged_files

    View Slide

  26. Directory task’leri
    # directory "merged_files"
    file "merged_files" do |t|
    mkdir t.name
    end
    $ rake merged_files

    View Slide

  27. Directory task’leri
    directory "merged_files"
    file "merged.txt" => ["merged_files", "file1.txt", "file2.txt"] do |t|
    folder_name = t.prerequisites.first
    file_names = t.prerequisites[1 ..].join(' ')
    sh "cat #{file_names} > #{folder_name}/ #{t.name}"
    end
    $ rake merged.txt
    cat file1.txt file2.txt > merged_files/merged.txt

    View Slide

  28. FileUtils: `ri FileUtils`
    • mkdir
    • mkdir_p
    • rm
    • rm_r
    • rm_rf
    • rm_dir
    • touch
    • ln
    • ln_s
    • ln_sf
    • cp
    • cp_r
    • mv
    • chmod

    View Slide

  29. Argüman alan task’ler
    desc "greet user"
    task :greet, [:user_name] do |_, args|
    puts "hello #{args.user_name}"
    end
    $ rake greet["vigo"] # ya da
    $ rake greet[vigo] # zsh: rake greet\[vigo\]
    hello vigo
    $ rake greet["vigo kadriyani"] # space karakteri varsa
    $ rake "greet[vigo kadriyani]”

    View Slide

  30. Argüman alan task’ler
    desc "greet user"
    task :greet, [:user_name] do |_, args|
    args.with_defaults(user_name: "unknown person")
    puts "hello #{args.user_name}"
    end
    $ rake -T
    rake greet[user_name] # greet user
    $ rake greet # parametre/argüman olmadan
    hello unknown person
    $ rake greet[vigo]
    hello vigo

    View Slide

  31. Paralel çalışan task’ler
    multitask copy_files: %w[copy_src copy_doc copy_bin] do
    puts "All Copies Complete"
    end
    task :copy_src do
    puts "copy src task"
    end
    task :copy_doc do
    puts "copy doc task"
    end
    task :copy_bin do
    puts "copy bin task"
    end
    $ rake copy_files
    copy bin task
    copy src task
    copy doc task
    All Copies Complete

    View Slide

  32. rule
    # önceki kod ...
    %W[doc-1.md doc-2.md doc-3.md].each do |md_file|
    html_file = File.basename(md_file, ".md") + ".html"
    file html_file => md_file do
    puts "convert markdown: #{md_file} to html: #{html_file}"
    touch html_file
    end
    end
    # rake doc-1.html
    task :clean do
    rm Dir.glob('*.html')
    end
    task :default => :html
    task :html => %W[doc-1.html doc-2.html doc-3.html]
    rule ".html" => ".md" do |t|
    puts "convert markdown: #{t.source} to html: #{t.name}"
    touch " #{t.name}"
    end
    $ rake clean
    $ rm doc-1.html doc-2.html doc-3.html # eğer varsa ...
    $ rake
    convert markdown: doc-1.md to html: doc-1.html
    touch doc-1.html
    convert markdown: doc-2.md to html: doc-2.html
    touch doc-2.html
    convert markdown: doc-3.md to html: doc-3.html
    touch doc-3.html

    View Slide

  33. Diğer özellikler: FileList
    files = Rake ::FileList["*.md", “*.html”]
    task :default => :html
    task :html do
    puts "files: #{files}"
    puts files
    end
    $ rake
    files: doc-1.md doc-2.md doc-3.md doc-1.html doc-2.html doc-3.html
    doc-1.md
    doc-2.md
    doc-3.md
    doc-1.html
    doc-2.html
    doc-3.html

    View Slide

  34. Diğer özellikler: invoke
    task :check_settings do
    Rake ::Task["check_virtualenv"].invoke
    Rake ::Task["check_django_env"].invoke
    end
    task :check_virtualenv do
    abort "Please activate your virtual environment" unless ENV['VIRTUAL_ENV']
    end
    task :check_django_env do
    abort "Please define DJANGO_ENV variable" unless ENV['DJANGO_ENV']
    end
    task :check_settings => [:check_virtualenv, :check_django_env] do |t|
    puts "task #{t.name} called ..."
    end
    task :check_virtualenv do
    abort "Please activate your virtual environment" unless ENV['VIRTUAL_ENV']
    end
    task :check_django_env do
    abort "Please define DJANGO_ENV variable" unless ENV['DJANGO_ENV']
    end

    View Slide

  35. Diğer özellikler: invoke
    task :default => [:run]
    task :run => [:step1, :step2] do
    Rake ::Task["step1"].invoke
    Rake ::Task["step1"].invoke
    Rake ::Task["step2"].invoke
    Rake ::Task["step2"].invoke
    end
    task :step1 do
    puts "step1 is called"
    end
    task :step2 do
    puts "step2 is called"
    end
    $ rake
    step1 is called
    step2 is called

    View Slide

  36. Diğer özellikler: execute
    task :default => [:run]
    task :run => [:step1, :step2] do
    Rake ::Task["step1"].execute
    Rake ::Task["step1"].execute
    Rake ::Task["step2"].execute
    Rake ::Task["step2"].execute
    end
    task :step1 do
    puts "step1 is called"
    end
    task :step2 do
    puts "step2 is called"
    end
    $ rake
    step1 is called
    step2 is called
    step1 is called
    step1 is called
    step2 is called
    step2 is called
    https://medium.com/@sampatbadhe/rake-task-invoke-or-execute-419cd689c3bd

    View Slide

  37. Diğer özellikler: namespace
    namespace :db do
    desc "Make migration"
    task :migrate do
    puts "running migrations ..."
    end
    namespace :private do
    desc "Delete database ..."
    task :zapp do
    puts "noooooooooooooo"
    end
    end
    end
    desc "Call migration"
    task :call_migrate => ["db:migrate"] do
    puts "called db:migrate"
    end
    $ rake -T
    rake call_migrate # Call migration
    rake db:migrate # Make migration
    rake db:private:zapp # Delete database ..
    $ rake call_migrate
    running migrations ...
    called db:migrate
    $ rake db:private:zapp
    noooooooooooooo

    View Slide

  38. Diğer özellikler: invoke + argüman
    namespace :db do
    task :delete_db_file, [:db_file] do |_, args|
    psql = %x{ command -v psql }
    if psql.empty?
    fn = "project/db/ #{args.db_file}.sqlite3"
    if File.exists?(fn)
    puts "Deleting: #{fn}"
    rm(fn)
    else
    puts " #{fn} not exists ..."
    end
    else
    puts "Dropping and re-creating PostgreSQL database"
    system "dropdb #{POSTGRES_DB_NAME} --if-exists"
    system "createdb #{POSTGRES_DB_NAME}"
    end
    end
    task :delete_development_db do
    Rake ::Task["db:delete_db_file"].invoke('development')
    end
    end

    View Slide

  39. Argüman ve bağımlılık
    task :check_settings do
    abort "environment variable FOO is not set" unless ENV['FOO']
    end
    desc "Run development server (default task)"
    task :run_server, [:port] => [:check_settings] do |_, args|
    args.with_defaults(:port => 8000)
    puts "python manage.py runserver_plus #{args.port} --nothreading"
    end
    $ FOO=1 rake run_server[9000]
    python manage.py runserver_plus 9000 --nothreading

    View Slide

  40. task :default => [:install]
    desc "Remove/Delete build ..."
    task :clean do
    rm_rf %w(build dist)
    rm_rf Dir.glob("*.egg-info")
    puts "Build files are removed ..."
    end
    desc "Install package for local development purpose"
    task :install => [:build] do
    system "pip install -e ."
    end
    desc "Build package"
    task :build => [:clean] do
    system "python setup.py sdist bdist_wheel"
    end
    namespace :upload do
    desc "Upload package to main distro (release)"
    task :main => [:build] do
    puts "Uploading package to MAIN distro ..."
    system "twine upload --repository pypi dist /*"
    end
    desc "Upload package to test distro"
    task :test => [:build] do
    puts "Uploading package to TEST distro ..."
    system "twine upload --repository testpypi dist /*"
    end
    end
    AVAILABLE_REVISIONS = ["major", "minor", "patch"]
    desc "Bump version"
    task :bump, [:revision] do |t, args|
    args.with_defaults(revision: "patch")
    abort "Please provide valid revision: #{AVAILABLE_REVISIONS.join(',')}" unless AVAILABLE_REVISIONS.include?(args.revision)
    system "bumpversion #{args.revision}"
    end
    https://github.com/vbyazilim/django-vb-baseapp

    View Slide

  41. Kaynaklar
    • http://www.virtuouscode.com/2014/04/21/rake-part-1-basics/
    • http://www.virtuouscode.com/2014/04/22/rake-part-2-file-lists/
    • http://www.virtuouscode.com/2014/04/23/rake-part-3-rules/
    • http://www.virtuouscode.com/2014/04/24/rake-part-4-pathmap/
    • http://www.virtuouscode.com/2014/04/25/rake-part-5-file-operations/
    • http://www.virtuouscode.com/2014/04/28/rake-part-6-clean-and-clobber/
    • http://www.virtuouscode.com/2014/04/29/rake-part-7-multitask/
    Avdi Grimm

    View Slide

  42. Kaynaklar
    • https://martinfowler.com/articles/rake.html
    • https://www.stuartellis.name/articles/rake/
    • https://lukaszwrobel.pl/blog/rake-tutorial/
    • https://www.youtube.com/watch?v=AFPWDzHWjEY
    • https://github.com/seattlerb/rake-remote_task
    • https://l.vigo.io/rakefile-example
    • https://pragprog.com/screencast/v-jwsceasy/source-control-made-easy

    View Slide