Bagaimana cara mendapatkan skrip di Makefile?

Apakah ada cara yang lebih baik untuk mendapatkan skrip, yang menetapkan env vars, dari dalam makefile?

FLAG ?= 0
ifeq ($(FLAG),0)
export FLAG=1
/bin/myshell -c '<source scripts here> ; $(MAKE) $@'
else
...targets...
endif

person brooksbp    schedule 21.09.2011    source sumber
comment
Kedengarannya yang sebenarnya Anda inginkan adalah menulis skrip yang sumber skrip Anda untuk menyetel envvars dan kemudian menjalankan make, daripada membuat skrip itu sendiri...   -  person Chris Dodd    schedule 22.09.2011
comment
Saya melihat jawaban yang bagus di sini: unix.stackexchange.com/a/235254/18152 dan di sini: blog.153.io/2016/04/18/ sumber-skrip-shell-sedang dibuat   -  person zx1986    schedule 24.12.2018


Jawaban (14)


Untuk menjawab pertanyaan seperti yang ditanyakan: Anda tidak bisa.

Masalah mendasarnya adalah bahwa proses anak tidak dapat mengubah lingkungan induknya. Shell mengatasi hal ini dengan tidak melakukan forking proses baru saat source'ing, namun hanya menjalankan perintah tersebut dalam inkarnasi shell saat ini. Itu berfungsi dengan baik, tetapi make bukan /bin/sh (atau apa pun shell skrip Anda) dan tidak memahami bahasa itu (selain kesamaan yang mereka miliki).

Chris Dodd dan Foo Bah telah membahas satu kemungkinan solusi, jadi saya akan menyarankan solusi lain (dengan asumsi Anda menjalankan GNU make): proses pasca skrip shell menjadi teks yang kompatibel dan sertakan hasilnya:

shell-variable-setter.make: shell-varaible-setter.sh
    postprocess.py @^

# ...
else
include shell-variable-setter.make
endif

detail berantakan dibiarkan sebagai latihan.

person dmckee --- ex-moderator kitten    schedule 22.09.2011
comment
Anda tidak dapat membuat skrip untuk keseluruhan Makefile, namun Anda dapat melakukannya untuk satu resep. - person Yaroslav Nikitenko; 16.08.2018
comment
Ya source compilationEnv.sh; $(CC) -c -o $< $^ atau serupa akan berfungsi sebagai garis tindakan. - person dmckee --- ex-moderator kitten; 04.04.2019
comment
Anda dapat menggunakan include .env (atau file nilai kunci apa pun) untuk membuat variabel dapat diakses oleh seluruh Makefile dan kemudian hanya meneruskan variabel yang diperlukan ke dalam resep masing-masing. - person rypel; 24.08.2019

Shell default Makefile adalah /bin/sh yang tidak mengimplementasikan source.

Mengubah shell menjadi /bin/bash memungkinkan:

# Makefile

SHELL := /bin/bash

rule:
    source env.sh && YourCommand
person PureW    schedule 23.04.2017
comment
@VivekAditya Tentu saja ini adalah hal yang sangat berguna. Namun perhatikan bahwa OP meminta untuk menyetel variabel untuk digunakan oleh make dan bukan hanya untuk digunakan dalam baris tindakan. Ini adalah cara melakukan yang terakhir, namun tidak ada cara langsung untuk melakukan yang pertama. - person dmckee --- ex-moderator kitten; 10.04.2019
comment
Anda dapat dengan mudah mengatur SHELL ke pembungkus Anda sendiri untuk mencapai hal ini. Tampil di sini gist.github.com/ingydotnet/99f7e259319f6b90e50a754f3053be7f - person ingydotnet; 24.09.2019

Jika tujuan Anda hanya menyetel variabel lingkungan untuk Make, mengapa tidak menyimpannya dalam sintaksis Makefile dan menggunakan perintah include?

include other_makefile

Jika Anda harus menjalankan skrip shell, ambil hasilnya dalam perintah shell:

JUST_DO_IT=$(shell source_script)

perintah shell harus dijalankan sebelum target. Namun ini tidak akan mengatur variabel lingkungan.

Jika Anda ingin mengatur variabel lingkungan dalam build, tulis skrip shell terpisah yang menjadi sumber variabel lingkungan dan panggilan Anda. Kemudian, di makefile, minta target memanggil skrip shell baru.

Misalnya, jika makefile asli Anda memiliki target a, maka Anda ingin melakukan sesuatu seperti ini:

# mysetenv.sh
#!/bin/bash
. <script to source>
export FLAG=1
make "$@" 

# Makefile
ifeq($(FLAG),0)
export FLAG=1
a: 
    ./mysetenv.sh a
else
a:
    .. do it
endif
person Foo Bah    schedule 21.09.2011
comment
Saya tidak punya kendali atas naskahnya. Harus sumbernya. - person brooksbp; 22.09.2011
comment
Apakah ini akan menangkap variabel lingkungan jika tidak diekspor? Dari pandangan pertama sepertinya itu memanggil shell baru, menjalankan perintah, lalu keluar dan kembali ke lingkungan lama..? - person brooksbp; 22.09.2011
comment
@brooksbp Perintah shell tidak akan menangkap variabel lingkungan. Namun, jika Anda menulis skrip shell yang mengambil sumber asli, maka itu akan terjadi. Saya akan menjelaskannya lebih lanjut - person Foo Bah; 22.09.2011

Menggunakan GNU Make 3.81 saya dapat mengambil skrip shell dari make menggunakan:

rule:
<tab>source source_script.sh && build_files.sh

build_files.sh "mendapatkan" variabel lingkungan yang diekspor oleh source_script.sh.

Perhatikan bahwa menggunakan:

rule:
<tab>source source_script.sh
<tab>build_files.sh

Tidak akan berhasil. Setiap baris dijalankan dalam subkulitnya sendiri.

person Samuel    schedule 31.12.2014
comment
Perhatikan bahwa menjaga source dan perintah pada baris yang sama menjadi penting dengan GNU Make 4.1 (dan mungkin 4.0 juga). Menggunakan 3.81, saya dapat menggunakan baris yang berbeda. Untuk portabilitas, satu baris berfungsi di seluruh versi ini. Namun, menjalankan skrip eksternal lebih mudah dibaca jika barisnya menjadi terlalu panjang! - person Eric Platon; 12.02.2018
comment
Apa kamu yakin? Beberapa baris tidak berfungsi untuk saya di 3.81, seperti yang saya sebutkan di jawaban. Make menjalankan setiap baris dalam subkulitnya sendiri, jadi beberapa baris tidak akan berfungsi pada versi apa pun. Mungkin Anda memiliki OS yang tidak umum atau buatan build? - person Samuel; 13.02.2018
comment
beberapa baris berfungsi selama Anda mengakhiri setiap baris dengan `\` - person user5359531; 10.10.2018
comment
@ user5359531 Ya, itu benar. - person Samuel; 10.10.2018

Ini berhasil untuk saya. Gantikan env.sh dengan nama file yang ingin Anda jadikan sumber. Ia bekerja dengan mengambil sumber file di bash dan mengeluarkan lingkungan yang dimodifikasi, setelah memformatnya, ke file bernama makeenv yang kemudian bersumber dari makefile.

IGNORE := $(shell bash -c "source env.sh; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv")                         
include makeenv   
person gdw2    schedule 10.05.2013
comment
Bagus untuk mengatur lingkungan kompiler yang benar (di sini 64-bit). Terima kasih. - person revher; 23.12.2014
comment
Apakah sintaks untuk menjalankan ini: make IGNORE? - person John; 01.10.2019
comment
@John, tidak. Hanya 1_. - person gdw2; 01.10.2019
comment
Bagaimana jika nilai variabel di env.sh Anda berisi karakter Make khusus seperti # atau $. Ketika Anda melakukan include makeenv nilai-nilai itu akan menjadi kacau. - person donhector; 07.02.2020

Beberapa konstruksi sama di shell dan GNU Make.

var=1234
text="Some text"

Anda dapat mengubah skrip shell Anda untuk menentukan sumbernya. Semuanya harus tipe name=value sederhana.

Ie,

[skrip.sh]

. ./vars.sh

[Makefile]

include vars.sh

Kemudian skrip shell dan Makefile dapat berbagi 'sumber' informasi yang sama. Saya menemukan pertanyaan ini karena saya sedang mencari manifes sintaksis umum yang dapat digunakan dalam skrip Gnu Make dan shell (saya tidak peduli shell yang mana).

Edit: Kerang dan pahami ${var}. Ini berarti Anda dapat menggabungkan, dll, var="One string" var=${var} "Second string"

person artless noise    schedule 05.12.2012
comment
Err, skrip shell seperti di atas tidak membuat detail berantakan seperti pada jawaban yang diterima. Hasilnya menjadi seperti file kbuild. - person artless noise; 28.01.2015
comment
Variabel shell dan variabel make tidak selalu dapat dipertukarkan secara transparan. Saat Anda include di Makefile Anda, mereka harus mematuhi sintaks make, yang mengharuskan karakter tertentu untuk di-escape. Misalnya nilai variabel shell seperti SECRET='12#34$56' di .vars.sh Anda akan menyebabkan masalah saat menggunakannya setelah include vars.sh - person donhector; 07.02.2020
comment
Menurut saya ada dua kasus penggunaan; OP tidak memiliki kendali atas skrip. Jadi maksud Anda sangat bagus karena kemungkinan besar Anda akan mendapatkan data yang perlu di-escape. Ini adalah %$#. Jika kasus penggunaannya adalah untuk berbagi variabel antar alat yang menggunakan variabel lingkungan dan membuat, maka solusi ini dapat berhasil. Keuntungannya hanya satu sumber informasi dan tidak ada proses tambahan. Jika Anda tidak memiliki kendali atas skrip shell, jawaban ini tidak berguna. Juga dipertanyakan mengapa Anda menginginkan $%# dalam nama variabel. Jika Anda menghitung item, Anda harus mengikuti bagian 'Edit:. - person artless noise; 08.02.2020

Saya sangat suka jawaban Foo Bah di mana skrip dipanggil, dan skrip dipanggil kembali untuk dibuat. Untuk memperluas jawaban itu saya melakukan ini:

# Makefile
.DEFAULT_GOAL := all 

ifndef SOME_DIR

%:
<tab>. ./setenv.sh $(MAKE) $@

else

all:
<tab>...

clean:
<tab>...

endif

--

# setenv.sh
export SOME_DIR=$PWD/path/to/some/dir

if [ -n "$1" ]; then
    # The first argument is set, call back into make.
    $1 $2
fi

Ini memiliki keuntungan tambahan menggunakan $(MAKE) jika ada orang yang menggunakan program make yang unik, dan juga akan menangani aturan apa pun yang ditentukan pada baris perintah, tanpa harus menduplikasi nama setiap aturan jika SOME_DIR tidak ditentukan .

person Samuel    schedule 10.03.2016
comment
Jika Anda memiliki versi make yang lebih lama dan .DEFAULT_GOAL tidak didukung maka cabang pertama ifndef akan gagal dengan make: *** Tidak ada target. Berhenti.. Anda dapat memperbaikinya dengan menambahkan semua ke aturan implisit, seperti ini: semua %:. Perhatikan bahwa ini akan menyebabkan *** peringatan aturan implisit dan normal campuran: peringatan sintaksis yang tidak digunakan lagi dengan versi make yang lebih baru. - person Samuel; 25.03.2016

Solusi saya untuk ini: (dengan asumsi Anda memiliki bash, sintaks untuk $@ berbeda untuk tcsh misalnya)

Miliki skrip sourceThenExec.sh, seperti:

#!/bin/bash
source whatever.sh
$@

Kemudian, di makefile Anda, awali target Anda dengan bash sourceThenExec.sh, misalnya:

ExampleTarget:
    bash sourceThenExec.sh gcc ExampleTarget.C

Anda tentu saja dapat meletakkan sesuatu seperti STE=bash sourceThenExec.sh di bagian atas makefile Anda dan mempersingkatnya:

ExampleTarget:
    $(STE) gcc ExampleTarget.C

Semua ini berfungsi karena sourceThenExec.sh membuka subkulit, tetapi kemudian perintah dijalankan di subkulit yang sama.

Kelemahan dari metode ini adalah file tersebut diambil dari setiap target, yang mungkin tidak diinginkan.

person Chris    schedule 13.02.2016

Jika Anda ingin memasukkan variabel ke dalam lingkungan, sehingga variabel tersebut diteruskan ke proses anak, maka Anda dapat menggunakan set -a dan set +a bash. Yang pertama berarti, "Saat saya menyetel variabel, setel juga variabel lingkungan yang sesuai." Jadi ini berfungsi untuk saya:

check:
    bash -c "set -a && source .env.test && set +a && cargo test"

Itu akan meneruskan semuanya di .env.test ke cargo test sebagai variabel lingkungan.

Perhatikan bahwa ini akan memungkinkan Anda meneruskan lingkungan ke sub-perintah, tetapi tidak memungkinkan Anda menyetel variabel Makefile (yang merupakan hal yang berbeda). Jika Anda memerlukan yang terakhir, Anda harus mencoba salah satu saran lain di sini.

person Paul A Jungwirth    schedule 01.03.2017

Jika Anda hanya memerlukan beberapa variabel yang diketahui, mengekspor dalam makefile dapat menjadi pilihan, berikut adalah contoh yang saya gunakan.

$ grep ID /etc/os-release 

ID=ubuntu
ID_LIKE=debian


$ cat Makefile

default: help rule/setup/lsb

source?=.

help:
        -${MAKE} --version | head -n1

rule/setup/%:
        echo ID=${@F}

rule/setup/lsb: /etc/os-release
        ${source} $< && export ID && ${MAKE} rule/setup/$${ID}


$ make

make --version | head -n1
GNU Make 3.81
. /etc/os-release && export ID && make rule/setup/${ID}
make[1]: Entering directory `/tmp'
echo ID=ubuntu
ID=ubuntu

-- http://rzr.online.fr/q/gnumake

person RzR    schedule 07.01.2016

Bergantung pada versi Make dan shell yang disertakan, Anda dapat menerapkan solusi yang bagus melalui eval, cat, dan panggilan berantai dengan &&:

ENVFILE=envfile

source-via-eval:
  @echo "FOO: $${FOO}"
  @echo "FOO=AMAZING!" > $(ENVFILE)
  @eval `cat $(ENVFILE)` && echo "FOO: $${FOO}"

Dan tes cepat:

> make source-via-eval
FOO:
FOO: AMAZING!
person tankthinks    schedule 21.08.2018

Solusi elegan dapat ditemukan di sini:

ifneq (,$(wildcard ./.env))
    include .env
    export
endif
person Sanpi    schedule 02.12.2020

Cara lain yang mungkin adalah dengan membuat skrip sh, misalnya run.sh, sumber skrip yang diperlukan dan panggil make di dalam skrip.

#!/bin/sh
source script1
source script2 and so on

make 
person Veera    schedule 19.08.2016

target: output_source
    bash ShellScript_name.sh

coba ini akan berhasil, skrip ada di dalam direktori saat ini.

person amit pandya    schedule 01.09.2017