จะหาสคริปต์ใน Makefile ได้อย่างไร

มีวิธีที่ดีกว่าในการจัดหาสคริปต์ซึ่งตั้งค่า env vars จากภายใน 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 แหล่งที่มา
comment
ดูเหมือนสิ่งที่คุณต้องการจริงๆ คือการเขียนสคริปต์ที่มาจากสคริปต์ของคุณเพื่อตั้งค่า envvars แล้วรัน make แทนที่จะสร้างสคริปต์ต้นฉบับเอง ...   -  person Chris Dodd    schedule 22.09.2011
comment
ฉันเห็นคำตอบที่ดีที่นี่: unix.stackexchange.com/a/235254/18152 และที่นี่: blog.153.io/2016/04/18/ แหล่งที่มา-a-shell-script-in-make   -  person zx1986    schedule 24.12.2018


คำตอบ (14)


หากต้องการตอบคำถามตามที่ถาม: คุณไม่สามารถ

ปัญหาพื้นฐานคือกระบวนการลูกไม่สามารถเปลี่ยนแปลงสภาพแวดล้อมของผู้ปกครองได้ เชลล์หลีกเลี่ยงสิ่งนี้โดย ไม่ฟอร์กกระบวนการใหม่ เมื่อ source'ing แต่เพียงแค่รันคำสั่งเหล่านั้นในรูปแบบปัจจุบันของเชลล์ ใช้งานได้ดี แต่ make ไม่ใช่ /bin/sh (หรือเชลล์สคริปต์ของคุณคืออะไร) และไม่เข้าใจภาษานั้น (นอกเหนือจากบิตที่พวกเขามีเหมือนกัน)

Chris Dodd และ Foo Bah ได้แก้ไขวิธีแก้ปัญหาที่เป็นไปได้อย่างหนึ่ง ดังนั้นฉันจะแนะนำวิธีอื่น (สมมติว่าคุณกำลังใช้งาน GNU make): โพสต์ประมวลผลเชลล์สคริปต์ให้เป็นข้อความที่เข้ากันได้และรวมผลลัพธ์ไว้ด้วย:

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

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

รายละเอียดยุ่งๆ เหลือไว้เป็นแบบฝึกหัด

person dmckee --- ex-moderator kitten    schedule 22.09.2011
comment
คุณไม่สามารถแหล่งที่มาของสคริปต์สำหรับ Makefile ทั้งหมดได้ แต่คุณสามารถทำได้สำหรับสูตรอาหารเดียว - person Yaroslav Nikitenko; 16.08.2018
comment
ใช่ source compilationEnv.sh; $(CC) -c -o $< $^ หรือคล้ายกันจะใช้เป็นบรรทัดการดำเนินการ - person dmckee --- ex-moderator kitten; 04.04.2019
comment
คุณสามารถใช้ include .env (หรือไฟล์คีย์-ค่าใดก็ได้) เพื่อทำให้ Makefile ทั้งหมดสามารถเข้าถึงตัวแปรได้ จากนั้นจึงส่งเฉพาะตัวแปรที่จำเป็นไปยังสูตรที่เกี่ยวข้อง - person rypel; 24.08.2019

เชลล์เริ่มต้นของ Makefile คือ /bin/sh ซึ่งไม่ได้ใช้ source

การเปลี่ยนเชลล์เป็น /bin/bash ทำให้เป็นไปได้:

# Makefile

SHELL := /bin/bash

rule:
    source env.sh && YourCommand
person PureW    schedule 23.04.2017
comment
@VivekAditya แน่นอนว่านี่เป็นสิ่งที่มีประโยชน์อย่างมาก แต่สังเกตว่า OP ขอให้ตั้งค่าตัวแปรสำหรับใช้งานโดย make และไม่ใช่แค่สำหรับใช้ในบรรทัดการดำเนินการเท่านั้น นี่คือวิธีการทำอย่างหลัง แต่ไม่มีวิธีโดยตรงที่จะทำอย่างหลัง - person dmckee --- ex-moderator kitten; 10.04.2019
comment
คุณสามารถตั้งค่า SHELL เป็น wrapper ของคุณเองได้อย่างง่ายดายเพื่อทำสิ่งนี้ให้สำเร็จ แสดงที่นี่ gist.github.com/ingydotnet/99f7e259319f6b90e50a754f3053be7f - person ingydotnet; 24.09.2019

หากเป้าหมายของคุณคือเพียงตั้งค่าตัวแปรสภาพแวดล้อมสำหรับ Make ทำไมไม่เก็บไว้ในไวยากรณ์ Makefile และใช้คำสั่ง include

include other_makefile

หากคุณต้องเรียกใช้เชลล์สคริปต์ ให้บันทึกผลลัพธ์ในคำสั่ง shell:

JUST_DO_IT=$(shell source_script)

คำสั่งเชลล์ควรทำงานก่อนเป้าหมาย อย่างไรก็ตาม สิ่งนี้จะไม่ตั้งค่าตัวแปรสภาพแวดล้อม

หากคุณต้องการตั้งค่าตัวแปรสภาพแวดล้อมในบิลด์ ให้เขียนเชลล์สคริปต์แยกต่างหากที่เป็นแหล่งตัวแปรสภาพแวดล้อมและการเรียกใช้ของคุณ จากนั้นใน makefile ให้เป้าหมายเรียกเชลล์สคริปต์ใหม่

ตัวอย่างเช่น หาก makefile ดั้งเดิมของคุณมีเป้าหมาย a คุณต้องการดำเนินการดังนี้:

# 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
ฉันไม่สามารถควบคุมสคริปต์ได้ ก็ต้องแหล่งที่มาสิ - person brooksbp; 22.09.2011
comment
สิ่งนี้จะจับตัวแปรสภาพแวดล้อมหรือไม่หากไม่ได้ส่งออก ดูเผินๆ ดูเหมือนว่ามันจะเรียกใช้เชลล์ใหม่ รันคำสั่ง จากนั้นออกและกลับสู่สภาพแวดล้อมเก่า..? - person brooksbp; 22.09.2011
comment
@brooksbp คำสั่งเชลล์จะไม่จับตัวแปรสภาพแวดล้อม แต่ถ้าคุณเขียนเชลล์สคริปต์ที่มาจากต้นฉบับ มันก็จะเป็นเช่นนั้น ฉันจะอธิบายเพิ่มเติม - person Foo Bah; 22.09.2011

การใช้ GNU Make 3.81 ฉันสามารถจัดหาเชลล์สคริปต์จาก make โดยใช้:

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

build_files.sh "รับ" ตัวแปรสภาพแวดล้อมที่ส่งออกโดย source_script.sh

โปรดทราบว่าการใช้:

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

จะไม่ทำงาน. แต่ละบรรทัดจะรันในเชลล์ย่อยของตัวเอง

person Samuel    schedule 31.12.2014
comment
โปรดทราบว่าการเก็บ source และคำสั่งไว้ในบรรทัดเดียวกันนั้นจำเป็นสำหรับ GNU Make 4.1 (และบางทีอาจเป็น 4.0 ด้วย) การใช้ 3.81 ฉันสามารถใช้บรรทัดที่แตกต่างกันได้ สำหรับการพกพา บรรทัดเดียวใช้ได้กับเวอร์ชันเหล่านี้ การเรียกใช้สคริปต์ภายนอกนั้นสามารถอ่านได้มากขึ้นหากบรรทัดยาวเกินไป! - person Eric Platon; 12.02.2018
comment
คุณแน่ใจไหม? หลายบรรทัดใช้ไม่ได้สำหรับฉันใน 3.81 อย่างที่ฉันพูดถึงในคำตอบ Make รันแต่ละบรรทัดในเชลล์ย่อยของตัวเอง ดังนั้นการมีหลายบรรทัดจึงใช้ไม่ได้กับเวอร์ชันใดๆ บางทีคุณอาจมีระบบปฏิบัติการที่ไม่ธรรมดาหรือสร้างบิลด์ขึ้นมา? - person Samuel; 13.02.2018
comment
หลายบรรทัดใช้งานได้ตราบใดที่คุณลงท้ายแต่ละบรรทัดด้วย `\` - person user5359531; 10.10.2018
comment
@ user5359531 ใช่ นั่นเป็นเรื่องจริง - person Samuel; 10.10.2018

สิ่งนี้ใช้ได้ผลสำหรับฉัน แทนที่ env.sh ด้วยชื่อไฟล์ที่คุณต้องการแหล่งที่มา มันทำงานโดยการจัดหาไฟล์ใน bash และส่งออกสภาพแวดล้อมที่แก้ไขหลังจากฟอร์แมตแล้ว ไปยังไฟล์ชื่อ makeenv ซึ่งต่อมามาจาก makefile

IGNORE := $(shell bash -c "source env.sh; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv")                         
include makeenv   
person gdw2    schedule 10.05.2013
comment
เหมาะสำหรับการตั้งค่าสภาพแวดล้อมคอมไพเลอร์ที่ถูกต้อง (ที่นี่ 64 บิต) ขอบคุณ - person revher; 23.12.2014
comment
ไวยากรณ์ที่จะดำเนินการนี้: ทำให้ IGNORE หรือไม่ - person John; 01.10.2019
comment
@จอห์น ไม่ แค่make. - person gdw2; 01.10.2019
comment
จะเกิดอะไรขึ้นถ้าค่าของตัวแปรใน env.sh ของคุณมีอักขระ Make พิเศษ เช่น # หรือ $ เมื่อคุณทำ include makeenv ค่าเหล่านั้นจะเละเทะไปหมด - person donhector; 07.02.2020

โครงสร้างบางอย่างเหมือนกันใน shell และใน GNU Make

var=1234
text="Some text"

คุณสามารถแก้ไขเชลล์สคริปต์ของคุณเพื่อกำหนดแหล่งที่มาได้ ทั้งหมดต้องเป็นประเภท name=value แบบง่าย

Ie,

[script.sh]

. ./vars.sh

[เมคไฟล์]

include vars.sh

จากนั้นเชลล์สคริปต์และ Makefile สามารถใช้ 'แหล่งที่มา' ของข้อมูลเดียวกันได้ ฉันพบคำถามนี้เพราะฉันกำลังมองหารายการของไวยากรณ์ทั่วไปที่สามารถใช้ใน Gnu Make และเชลล์สคริปต์ได้ (ฉันไม่สนใจว่าเชลล์ใด)

แก้ไข: เชลล์และทำให้เข้าใจ ${var} ซึ่งหมายความว่าคุณสามารถเชื่อมต่อ ฯลฯ var="One string" var=${var} "Second string"

person artless noise    schedule 05.12.2012
comment
เอ่อ เชลล์สคริปต์เหมือนกับที่กล่าวมาข้างต้นไม่สร้างรายละเอียดที่ยุ่งเหยิงเหมือนในคำตอบที่ยอมรับ สุดท้ายมันก็เหมือนกับไฟล์ kbuild มาก - person artless noise; 28.01.2015
comment
ตัวแปร shell และตัวแปร make ไม่สามารถใช้แทนกันได้อย่างโปร่งใสเสมอไป เมื่อคุณ include พวกเขาอยู่ใน Makefile ของคุณ พวกเขาจะต้องสอดคล้องกับไวยากรณ์ make ซึ่งกำหนดให้อักขระบางตัวต้องถูกหลีก ตัวอย่างเช่น ค่าตัวแปรเชลล์เช่น SECRET='12#34$56' ใน .vars.sh ของคุณจะทำให้คุณเกิดปัญหาเมื่อใช้งานหลังจาก include vars.sh - person donhector; 07.02.2020
comment
ฉันคิดว่ามีสองกรณีการใช้งาน OP ไม่มีการควบคุมสคริปต์ ประเด็นของคุณดีมากเนื่องจากคุณมีแนวโน้มที่จะได้รับข้อมูลที่ต้องหลบหนีมากที่สุด เหล่านี้คือ %$# หากกรณีการใช้งานคือการแชร์ตัวแปรระหว่างเครื่องมือที่ใช้ตัวแปรสภาพแวดล้อมและสร้าง โซลูชันนี้ก็สามารถทำงานได้ มีข้อได้เปรียบจากแหล่งข้อมูลเพียงแห่งเดียวและไม่มีกระบวนการเพิ่มเติม หากคุณไม่สามารถควบคุมเชลล์สคริปต์ได้ คำตอบนี้ไม่มีประโยชน์ ยังน่าสงสัยว่าทำไมคุณถึงต้องการ $%# ในชื่อตัวแปร หากคุณคำนวณรายการ คุณจะต้องทำตามส่วน "แก้ไข:" - person artless noise; 08.02.2020

ฉันชอบคำตอบของ Foo Bah มากที่โทรออกสคริปต์ และสคริปต์โทรกลับ เพื่อขยายคำตอบนั้นฉันทำสิ่งนี้:

# 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

นี่เป็นข้อดีเพิ่มเติมของการใช้ $(MAKE) ในกรณีที่มีใครใช้โปรแกรม make ที่ไม่ซ้ำใคร และจะจัดการกฎใดๆ ที่ระบุบนบรรทัดคำสั่งด้วย โดยไม่ต้องทำซ้ำชื่อของแต่ละกฎในกรณีที่ไม่ได้กำหนด SOME_DIR .

person Samuel    schedule 10.03.2016
comment
หากคุณมี make เวอร์ชันเก่ากว่าและไม่รองรับ .DEFAULT_GOAL สาขาแรกของ ifndef จะล้มเหลวด้วย make: *** ไม่มีเป้าหมาย หยุด.. คุณสามารถแก้ไขได้โดยเพิ่มทั้งหมดลงในกฎโดยนัย เช่นนี้: all %: โปรดทราบว่าสิ่งนี้จะทำให้เกิด *** ผสมโดยนัยและกฎปกติ: คำเตือนไวยากรณ์ที่เลิกใช้แล้วพร้อมกับ make เวอร์ชันที่ใหม่กว่า - person Samuel; 25.03.2016

วิธีแก้ปัญหาของฉันสำหรับสิ่งนี้: (สมมติว่าคุณมี bash ไวยากรณ์สำหรับ $@ จะแตกต่างสำหรับ tcsh เป็นต้น)

มีสคริปต์ sourceThenExec.sh เช่น:

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

จากนั้นใน makefile ของคุณ นำหน้าเป้าหมายของคุณด้วย bash sourceThenExec.sh เช่น:

ExampleTarget:
    bash sourceThenExec.sh gcc ExampleTarget.C

แน่นอนคุณสามารถใส่บางอย่างเช่น STE=bash sourceThenExec.sh ที่ด้านบนของ makefile ของคุณและย่อให้สั้นลง:

ExampleTarget:
    $(STE) gcc ExampleTarget.C

ทั้งหมดนี้ได้ผลเพราะ sourceThenExec.sh เปิดเชลล์ย่อย แต่คำสั่งต่างๆ จะถูกรันในเชลล์ย่อยเดียวกัน

ข้อเสียของวิธีนี้คือไฟล์ได้รับแหล่งที่มาสำหรับแต่ละเป้าหมายซึ่งอาจไม่เป็นที่พึงปรารถนา

person Chris    schedule 13.02.2016

หากคุณต้องการให้ตัวแปรเข้าสู่ สภาพแวดล้อม เพื่อให้ตัวแปรเหล่านั้นถูกส่งไปยังกระบวนการลูก คุณสามารถใช้ set -a และ set +a ของ bash แบบแรกหมายถึง "เมื่อฉันตั้งค่าตัวแปร ให้ตั้งค่าตัวแปรสภาพแวดล้อมที่เกี่ยวข้องด้วย" สิ่งนี้ใช้ได้กับฉัน:

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

นั่นจะส่งทุกอย่างใน .env.test ไปยัง cargo test เป็นตัวแปรสภาพแวดล้อม

โปรดทราบว่าการดำเนินการนี้จะช่วยให้คุณสามารถส่งผ่านสภาพแวดล้อมไปยังคำสั่งย่อยได้ แต่จะไม่อนุญาตให้คุณตั้งค่าตัวแปร Makefile (ซึ่งมีความแตกต่างกันอยู่แล้ว) หากคุณต้องการอย่างหลัง คุณควรลองใช้คำแนะนำอื่นๆ ที่นี่

person Paul A Jungwirth    schedule 01.03.2017

หากคุณต้องการตัวเลือกตัวแปรที่รู้จักเพียงไม่กี่ตัวที่ส่งออกใน makefile ได้ นี่คือตัวอย่างของสิ่งที่ฉันใช้

$ 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

ขึ้นอยู่กับเวอร์ชันของ Make และการปิดเชลล์ คุณสามารถใช้โซลูชันที่ดีผ่านทาง eval, cat และการโทรแบบผูกมัดด้วย &&:

ENVFILE=envfile

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

และการทดสอบอย่างรวดเร็ว:

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

พบโซลูชันที่ยอดเยี่ยมได้ที่นี่:

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

อีกวิธีที่เป็นไปได้คือการสร้างสคริปต์ sh เช่น run.sh แหล่งที่มาของสคริปต์ที่จำเป็นและการโทรภายในสคริปต์

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

make 
person Veera    schedule 19.08.2016

target: output_source
    bash ShellScript_name.sh

ลองทำเช่นนี้ มันจะได้ผล สคริปต์อยู่ในไดเร็กทอรีปัจจุบัน

person amit pandya    schedule 01.09.2017