วิธีนำเข้าแพคเกจท้องถิ่นโดยไม่มี gopath


171

ฉันเคยใช้GOPATHแต่สำหรับปัญหาปัจจุบันนี้ฉันกำลังเผชิญหน้ากับมันไม่ได้ช่วย ฉันต้องการสร้างแพ็คเกจที่เฉพาะเจาะจงกับโครงการ:

myproject/
├── binary1.go
├── binary2.go
├── package1.go
└── package2.go

ฉันลองหลายวิธี แต่ฉันpackage1.goจะทำงานในbinary1.goหรือต่อbinary2.goๆ ไปได้อย่างไร

ตัวอย่างเช่น; ฉันต้องการที่จะสามารถimport "package1"แล้วสามารถทำงานได้go build binary1.goและทุกอย่างทำงานได้ดีไม่มีข้อผิดพลาดที่ถูกโยนว่าแพคเกจไม่สามารถพบได้บนหรือGOROOT GOPATHเหตุผลที่ฉันต้องการฟังก์ชั่นประเภทนี้สำหรับโครงการขนาดใหญ่ ฉันไม่ต้องการอ้างอิงหลายแพ็คเกจหรือเก็บไว้ในไฟล์ขนาดใหญ่


2
คุณควรใส่ซอร์สไฟล์สำหรับแต่ละไบนารี่ลงในไดเรกทอรีของมัน
fuz

ทุก.goไฟล์ในไดเรกทอรีเดียวเป็นส่วนหนึ่งของแพ็กเกจเดิมและคุณไม่จำเป็นต้องimportไฟล์ในแพคเกจเดียวกัน (เช่นไดเรกทอรีเดียวกัน) คุณพูดถึงการทำงานนอก GOPATH ซึ่งเป็นหนึ่งในความสามารถของระบบโมดูล Go ใหม่ นี้คำตอบที่ครอบคลุมโมดูลโครงสร้างการนำเข้าแพคเกจท้องถิ่นจัดแพคเกจภายในโมดูลหรือไม่ที่จะมีโมดูลหลายในพื้นที่เก็บข้อมูลเดียว ฯลฯ
typical182

3
และพฤติกรรมนี้ก็โอเคกับทุกคน? โดยทั่วไปคุณไม่สามารถนำเข้าแพ็คเกจย่อยในเครื่องของคุณได้หากคุณไม่ระบุgit/repo/to/my/projectเส้นทางทั้งหมด ฉันไม่เห็นเหตุผลที่ใคร ๆ ก็ต้องการพฤติกรรมนี้ ถ้าคุณย้ายโครงการของคุณไปยังตำแหน่งอื่น (เช่นอิมเมจ Docker) คุณต้องเปลี่ยนเส้นทางทั้งหมดอีกครั้ง ฉันกำลังมองหาคำตอบว่าทำไมมันซับซ้อนมาก
milosmns

@milosmns ดูคำตอบของฉันstackoverflow.com/a/60915633/175071
Timo Huovinen

คำตอบ:


176

สรุปการจัดการการพึ่งพา:

  • vgo ถ้าเวอร์ชั่น go ของคุณคือ: x >= go 1.11
  • depหรือvendorถ้าเวอร์ชั่นที่คุณใช้คือ:go 1.6 >= x < go 1.11
  • ด้วยตนเองหากรุ่นที่คุณใช้คือ: x < go 1.6

แก้ไข 3: Go 1.11 มีคุณลักษณะที่vgoซึ่งจะมาแทนที่ dep

วิธีใช้vgoโปรดดูเอกสารประกอบโมดูล TLDR ด้านล่าง:

export GO111MODULE=on
go mod init
go mod vendor # if you have vendor/ folder, will automatically integrate
go build

วิธีนี้จะสร้างไฟล์ที่เรียกว่าgo.modในไดเรกทอรีโครงการของคุณ go buildจากนั้นคุณสามารถสร้างโครงการของคุณด้วย หากGO111MODULE=autoตั้งไว้แสดงว่าโครงการของคุณไม่สามารถเข้า$GOPATHร่วมได้


แก้ไข 2: วิธีการขายคืนยังคงใช้ได้และทำงานได้โดยไม่มีปัญหา vendorส่วนใหญ่เป็นกระบวนการแบบแมนนวลเนื่องจากสิ่งนี้depและvgoถูกสร้างขึ้น


แก้ไข 1: ในขณะที่วิธีการแบบเก่าของฉันทำงานได้ไม่เป็นวิธี "แก้ไข" อีกต่อไป คุณควรใช้ความสามารถของผู้ขายvgoหรือdep(ตอนนี้) ที่เปิดใช้งานโดยค่าเริ่มต้นใน Go 1.6; เห็นไหม โดยทั่วไปคุณจะเพิ่มแพ็คเกจ "ภายนอก" หรือ "พึ่งพา" ภายในvendorไดเรกทอรี ผู้รวบรวมจะใช้แพ็คเกจเหล่านี้ก่อน


พบ ผมก็นำเข้าแพคเกจในท้องถิ่นสามารถด้วยGOPATHโดยการสร้างโฟลเดอร์ย่อยของpackage1แล้วนำเข้าที่มีimport "./package1"ในbinary1.goและbinary2.goสคริปต์เช่นนี้

binary1.go

...
import (
        "./package1"
      )
...

ดังนั้นโครงสร้างไดเรกทอรีปัจจุบันของฉันจะเป็นดังนี้:

myproject/
├── binary1.go
├── binary2.go
├── package1/
   └── package1.go
└── package2.go

ฉันควรทราบด้วยว่าเส้นทางสัมพัทธ์ (อย่างน้อยในระหว่างการเดินทาง 1.5) ก็ใช้ได้เช่นกัน ตัวอย่างเช่น:

import "../packageX"

4
ใช้งานได้ดีจนกว่าคุณจะมีสองโฟลเดอร์ย่อยโดยที่หนึ่งอ้างอิงถึงอีกโฟลเดอร์หนึ่ง ตัวอย่างเช่นถ้า package2 เป็นโฟลเดอร์ย่อยและต้องการ package1 ระบบจะหยุดทำงาน
Carl

7
import "../package1"
เฟลิกซ์ Rabe

12
เส้นทางนำเข้าที่เป็นความคิดที่ดี
Dave C

1
หาก #golang ให้ 'namespace' ฉันสามารถเห็นด้วยกับคุณว่า 'เส้นทางการนำเข้าที่เกี่ยวข้อง' หรือ 'แพ็คเกจย่อย' เป็นแนวคิดที่ไม่ดี '
mission.liao

1
ชื่อฟังก์ชั่นควรเริ่มต้นด้วยคำสำคัญตัวพิมพ์ใหญ่
kokemomuke

71

ไม่มีสิ่งเช่น "แพคเกจท้องถิ่น" องค์กรของแพคเกจบนดิสก์เป็นฉากกับความสัมพันธ์แม่ / ลูกของแพคเกจ ลำดับชั้นที่แท้จริงเท่านั้นที่เกิดขึ้นโดยแพคเกจเป็นต้นไม้พึ่งพาซึ่งในกรณีทั่วไปไม่สะท้อนให้เห็นถึงต้นไม้ไดเรกทอรี

เพียงแค่ใช้

import "myproject/packageN"

และอย่าต่อสู้กับระบบการสร้างโดยไม่มีเหตุผลที่ดี การบันทึกอักขระหนึ่งโหลต่อการอิมพอร์ตในโปรแกรมที่ไม่สำคัญไม่ใช่เหตุผลที่ดีเพราะตัวอย่างเช่นโครงการที่มีเส้นทางการนำเข้าแบบสัมพัทธ์ไม่ใช่ go-gettable

แนวคิดของเส้นทางการนำเข้ามีคุณสมบัติที่สำคัญบางประการ:

  • เส้นทางนำเข้าอาจไม่ซ้ำกันทั่วโลก
  • เมื่อใช้ร่วมกับ GOPATH เส้นทางการอิมพอร์ตสามารถแปลอย่างไม่น่าสงสัยไปยังเส้นทางไดเรกทอรี
  • เส้นทางไดเรกทอรีใด ๆ ภายใต้ GOPATH สามารถแปลเป็นเส้นทางการนำเข้าได้อย่างไม่น่าสงสัย

ทั้งหมดข้างต้นถูกทำลายโดยใช้เส้นทางการนำเข้าที่เกี่ยวข้อง อย่าทำมัน.

PS: มีอยู่ไม่กี่แห่งในรหัสดั้งเดิมในการทดสอบคอมไพเลอร์ซึ่งใช้การนำเข้าแบบสัมพัทธ์ ATM นี่คือเหตุผลเดียวที่สนับสนุนการนำเข้าแบบสัมพัทธ์ทั้งหมด


2
ผมขอแนะนำการดูวิดีโอแนะนำนี้เพื่อความเข้าใจที่ดีขึ้นของแพคเกจและGOPATH youtube.com/watch?v=XCsL89YtqCs
Joshua Pinter

7
ฉันคิดว่านี่เป็นคำแนะนำที่ไม่ดี หากคุณลงเอยด้วยการใช้ gopkg.in สำหรับการกำหนดเวอร์ชันคุณไม่มีโชคกับเส้นทางการนำเข้าที่แน่นอนสำหรับการ "มินิ" pakages ตามที่อธิบายไว้ข้างต้น ไม่ว่าคุณจะทำลาย repo แหล่งที่มาหรือรุ่นที่ไม่มีประโยชน์
เกร็ก

import "myproject/packageN". myprojectชื่อโฟลเดอร์ที่ถือโครงการของฉันคืออะไร
securecurve

มันผิดอย่างสิ้นเชิงฉันจะใช้มันกับที่เก็บส่วนตัวได้อย่างไร
agilob

44

บางทีคุณกำลังพยายามทำให้โมดูลของคุณเป็นแบบแพคเกจ ฉันสมมติว่าpackage1และpackage2เป็นในทางที่เป็นส่วนหนึ่งของแพคเกจเดียวกัน แต่สำหรับการอ่านคุณกำลังแยกผู้เป็นหลายไฟล์

หากกรณีก่อนหน้านี้เป็นของคุณคุณสามารถใช้ชื่อแพคเกจเดียวกันในไฟล์ทวีคูณเหล่านั้นและมันจะเป็นถ้ามีไฟล์เดียวกัน

นี่คือตัวอย่าง:

add.go

package math

func add(n1, n2 int) int {
   return n1 + n2
}

subtract.go

package math

func subtract(n1, n2 int) int {
    return n1 - n2
}

donothing.go

package math

func donothing(n1, n2 int) int {
    s := add(n1, n2)
    s = subtract(n1, n2)
    return s
}

ฉันไม่ใช่ผู้เชี่ยวชาญ Go และนี่คือโพสต์แรกของฉันใน StackOveflow ดังนั้นหากคุณมีคำแนะนำจะได้รับการตอบรับอย่างดี


23

ฉันมีปัญหาที่คล้ายกันและวิธีแก้ปัญหาที่ฉันใช้ในปัจจุบันใช้โมดูล Go 1.11 ฉันมีโครงสร้างดังต่อไปนี้

- projects
  - go.mod
  - go.sum
  - project1
    - main.go
  - project2
    - main.go
  - package1
    - lib.go
  - package2
    - lib.go

และฉันสามารถนำเข้า package1 และ package2 จาก project1 และ project2 โดยใช้

import (
    "projects/package1"
    "projects/package2"
)

go mod init projectsหลังจากทำงาน ฉันสามารถใช้go buildจากไดเรกทอรี project1 และ project2 หรือฉันสามารถทำได้go build -o project1/exe project1/*.goจากไดเรกทอรีโครงการ

ข้อเสียของวิธีนี้คือโปรเจ็กต์ทั้งหมดของคุณจบลงด้วยการแชร์รายการการพึ่งพาเดียวกันใน go.mod ฉันยังคงมองหาวิธีการแก้ไขปัญหานี้ แต่ดูเหมือนว่ามันอาจเป็นพื้นฐาน


9

ตั้งแต่เปิดตัวgo.modฉันคิดว่าการจัดการแพคเกจทั้งในและนอกจะง่ายขึ้น เมื่อใช้go.modก็เป็นไปได้ที่จะทำโปรเจกต์นอก GOPATH เช่นกัน

นำเข้าแพคเกจท้องถิ่น:

สร้างโฟลเดอร์demoprojectและเรียกใช้คำสั่งต่อไปนี้เพื่อสร้างไฟล์go.mod

go mod init demoproject

ฉันมีโครงสร้างโครงการเหมือนด้านล่างในไดเรกทอรีdemoproject

├── go.mod
└── src
    ├── main.go
    └── model
        └── model.go

เพื่อวัตถุประสงค์ในการสาธิตให้ใส่รหัสต่อไปนี้ในไฟล์model.go

package model

type Employee struct {
    Id          int32
    FirstName   string
    LastName    string
    BadgeNumber int32
}

ในmain.goฉันนำเข้าโมเดลพนักงานโดยอ้างอิงถึง "demoproject / src / model"

package main

import (
    "demoproject/src/model"
    "fmt"
)

func main() {
    fmt.Printf("Main Function")

    var employee = model.Employee{
        Id:          1,
        FirstName:   "First name",
        LastName:    "Last Name",
        BadgeNumber: 1000,
    }
    fmt.Printf(employee.FirstName)
}

นำเข้าการพึ่งพาภายนอก:

เพียงแค่เรียกใช้go getคำสั่งภายในไดเรกทอรีโครงการ

ตัวอย่างเช่น:

go get -u google.golang.org/grpc

ควรรวมถึงการพึ่งพาโมดูลในไฟล์ go.mod

module demoproject

go 1.13

require (
    golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect
    golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 // indirect
    golang.org/x/text v0.3.2 // indirect
    google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150 // indirect
    google.golang.org/grpc v1.26.0 // indirect
)

https://blog.golang.org/using-go-modules


can't load package: package .: no Go files in...(ไปสร้างในโฟลเดอร์ของ go.mod)
Sebi2020

ความดุร้ายอย่างนี้ แต่ใช้เวลาพอสมควรในการหาคำตอบและโพสต์ของคุณนั้นอ่านง่ายและมีประโยชน์มากที่สุด ขอบคุณ!
Harold Cavendish

8

ในการเพิ่มแพ็คเกจ "ท้องถิ่น" ให้กับโครงการของคุณให้เพิ่มโฟลเดอร์ (เช่น "package_name") และนำไฟล์การติดตั้งไปใช้ในโฟลเดอร์นั้น

src/github.com/GithubUser/myproject/
 ├── main.go
 └───package_name
       └── whatever_name1.go
       └── whatever_name2.go

ในสิ่งที่คุณpackage mainทำ:

import "github.com/GithubUser/myproject/package_name"

package_nameชื่อโฟลเดอร์อยู่ที่ไหนและจะต้องตรงกับชื่อแพ็กเกจที่ใช้ในไฟล์ใด ๆ ก็ตาม _ ชื่อ _1 กล่าวอีกนัยหนึ่งไฟล์ทั้งหมดที่มีไดเรกทอรีย่อยควรอยู่ในแพ็คเกจเดียวกัน

คุณสามารถซ้อนไดเร็กทอรีย่อยเพิ่มเติมเพิ่มเติมได้ตราบใดที่คุณระบุพา ธ ทั้งหมดไปยังโฟลเดอร์พาเรนต์ในการนำเข้า


2
นี่เป็นข้อเสนอแนะที่ดียกเว้นว่าในระหว่างเคอร์เนลใด ๆ จะตกใจการติดตามสแต็กที่ถูกทิ้งจากไบนารีแสดงเส้นทาง github.com เช่นไม่ใช่พฤติกรรมที่พึงประสงค์ที่สุดเสมอไป มีแฟล็กที่จะระงับสิ่งนี้ แต่ก็ไม่จำเป็นที่จะต้องทำเพื่อให้ได้แพ็คเกจขององค์กรที่เรียบง่าย
Kenny Powers

package myproject/package_name is not in GOROOT (/usr/lib/go-1.14/src/myproject/package_name)
Sebi2020

3

คุณสามารถใช้ได้ replace

go modo init example.com/my/foo

foo / go.mod

module example.com/my/foo

go 1.14

replace example.com/my/bar => /path/to/bar

require example.com/my/bar v1.0.0

foo / main.go

package main
import "example.com/bar"

func main() {
    bar.MyFunc()
}

บาร์ / go.mod

module github.com/my/bar

go 1.14

บาร์ / fn.go

package github.com/my/bar

import "fmt"

func MyFunc() {
    fmt.Printf("hello")
}

การนำเข้าแพคเกจท้องถิ่นก็เหมือนกับการนำเข้าแพคเกจภายนอก

ยกเว้นในไฟล์ go.mod คุณแทนที่ชื่อแพ็กเกจภายนอกด้วยโฟลเดอร์โลคัล

เส้นทางไปยังโฟลเดอร์สามารถเป็นแบบเต็มหรือแบบสัมพันธ์ "/ path / to / bar" หรือ "../bar"

https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive https://thewebivore.com/using-replace-in-go-mod-to-point -to- ของคุณท้องถิ่นโมดูล /

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.