Làm thế nào để nhập gói địa phương mà không cần gopath


171

Tôi đã sử dụng GOPATHnhưng đối với vấn đề hiện tại này, tôi không phải đối mặt với nó. Tôi muốn có thể tạo các gói dành riêng cho một dự án:

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

Tôi đã thử nhiều cách nhưng làm thế nào để tôi package1.golàm việc trong binary1.gohoặc binary2.gov.v.

Ví dụ; Tôi muốn có thể import "package1"và sau đó có thể chạy go build binary1.govà mọi thứ hoạt động tốt mà không bị lỗi mà gói không thể được tìm thấy trên GOROOThoặc GOPATH. Lý do tại sao tôi cần loại chức năng này là cho các dự án quy mô lớn; Tôi không muốn phải tham khảo nhiều gói khác hoặc giữ chúng trong một tệp lớn.


2
Bạn nên đặt các tệp nguồn cho mỗi nhị phân vào thư mục riêng của nó.
fuz

Tất cả các .gotệp trong một thư mục là một phần của cùng một gói và bạn không cần phải gửi importcác tệp trong cùng một gói (nghĩa là cùng một thư mục). Bạn đã đề cập đến việc làm việc bên ngoài GOPATH, một trong những khả năng của hệ thống mô-đun Go mới. Đây câu trả lời bìa mô-đun cấu trúc, import các gói địa phương, sắp xếp các gói trong một mô-đun, có hoặc không có nhiều module trong kho lưu trữ duy nhất, vv
typical182

3
Và hành vi này là ok với tất cả mọi người? Rằng bạn về cơ bản không thể nhập các gói phụ cục bộ trừ khi bạn chỉ định toàn bộ git/repo/to/my/projectđường dẫn? Tôi chỉ không thấy lý do tại sao bất cứ ai cũng muốn hành vi này. Điều gì sẽ xảy ra nếu bạn di chuyển dự án của mình đến một vị trí khác (ví dụ hình ảnh Docker), bạn cần thay đổi lại tất cả các đường dẫn? Tôi đang tìm kiếm câu trả lời tại sao điều này rất phức tạp.
milosmns

@milosmns xem câu trả lời của tôi stackoverflow.com/a/60915633/175071
Timo Huovinen

Câu trả lời:


175

Tóm tắt quản lý phụ thuộc:

  • vgo nếu phiên bản đi của bạn là: x >= go 1.11
  • dephoặc vendornếu phiên bản đi của bạn là:go 1.6 >= x < go 1.11
  • Thủ công nếu phiên bản đi của bạn là: x < go 1.6

Chỉnh sửa 3: Go 1.11 có một tính năng vgosẽ thay thế dep .

Để sử dụng vgo, xem tài liệu Mô-đun . TLDR dưới đây:

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

Phương pháp này tạo ra một tệp được gọi go.modtrong thư mục dự án của bạn. Sau đó bạn có thể xây dựng dự án của bạn với go build. Nếu GO111MODULE=autođược đặt, thì dự án của bạn không thể ở trong đó $GOPATH.


Chỉnh sửa 2: Phương thức trả thù vẫn còn hiệu lực và hoạt động không có vấn đề. vendorphần lớn là một quy trình thủ công, vì điều này depvgođã được tạo ra.


Chỉnh sửa 1: Trong khi cách cũ của tôi hoạt động, nó không còn là cách "chính xác" để làm điều đó. Bạn nên sử dụng các khả năng của nhà cung cấpvgo hoặc dep(hiện tại) được bật theo mặc định trong Go 1.6; thấy . Về cơ bản, bạn thêm các gói "bên ngoài" hoặc "phụ thuộc" trong một vendorthư mục; khi biên dịch, trình biên dịch sẽ sử dụng các gói này trước.


Tìm. Tôi đã có thể nhập gói địa phương với GOPATHbằng cách tạo ra một thư mục con của package1và sau đó nhập khẩu với import "./package1"trong binary1.gobinary2.gokịch bản như thế này:

binary1.go

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

Vì vậy, cấu trúc thư mục hiện tại của tôi trông như thế này:

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

Tôi cũng nên lưu ý rằng các đường dẫn tương đối (ít nhất là trong 1.5) cũng hoạt động; ví dụ:

import "../packageX"

4
Điều đó hoạt động tốt cho đến khi bạn có hai thư mục con với một tham chiếu đến một thư mục khác. Ví dụ, nếu gói2 cũng là thư mục con và nó cần gói1, hệ thống sẽ bị hỏng.
Carl

7
import "../package1"
Felix Rabe

12
Đường dẫn nhập khẩu tương đối là một ý tưởng tồi .
Dave C

1
nếu #golang cung cấp 'không gian tên', tôi có thể đồng ý với bạn rằng 'đường dẫn nhập tương đối' hoặc 'gói phụ' là một ý tưởng tồi '.
nhiệm vụ.liao

1
Tên hàm nên bắt đầu bằng từ khóa
Capitilized

71

Không có thứ gọi là "gói địa phương". Việc tổ chức các gói trên đĩa là trực giao với mọi quan hệ cha / con của các gói. Hệ thống phân cấp thực sự duy nhất được hình thành bởi các gói là cây phụ thuộc, trong trường hợp chung không phản ánh cây thư mục.

Chỉ dùng

import "myproject/packageN"

và không chiến đấu với hệ thống xây dựng không có lý do chính đáng. Lưu hàng tá ký tự cho mỗi lần nhập trong bất kỳ chương trình không tầm thường nào không phải là lý do chính đáng, bởi vì, ví dụ, các dự án có đường dẫn nhập tương đối không thể thực hiện được.

Khái niệm đường dẫn nhập có một số thuộc tính quan trọng:

  • Đường dẫn nhập khẩu có thể là duy nhất trên toàn cầu.
  • Kết hợp với GOPATH, đường dẫn nhập có thể được dịch rõ ràng sang đường dẫn thư mục.
  • Bất kỳ đường dẫn thư mục nào trong GOPATH đều có thể được dịch rõ ràng sang đường dẫn nhập.

Tất cả những điều trên bị hủy hoại bằng cách sử dụng các đường dẫn nhập tương đối. Đừng làm việc đó.

PS: Có vài vị trí trong mã kế thừa trong các bài kiểm tra trình biên dịch Go sử dụng nhập khẩu tương đối. ATM, đây là lý do duy nhất tại sao nhập khẩu tương đối được hỗ trợ cả.


2
Tôi khuyên bạn nên xem video giới thiệu này để hiểu rõ hơn về các góiGOPATH . youtube.com/watch?v=XCsL89YtqCs
Joshua Pinter

7
Tôi nghĩ rằng đây là lời khuyên tồi. Ví dụ, nếu bạn kết thúc sử dụng gopkg.in để tạo phiên bản, bạn sẽ không gặp may với các đường dẫn nhập tuyệt đối cho các trang web "mini" của mình, như được mô tả ở trên. Hoặc bạn phá vỡ repo nguồn hoặc phiên bản đã trở thành vô dụng.
Greg

import "myproject/packageN". myprojecttên thư mục chứa dự án của tôi?
bảo đảm

Điều đó hoàn toàn sai, làm thế nào để tôi sử dụng nó với các kho riêng bây giờ?
agilob

44

Có lẽ bạn đang cố gắng mô đun hóa gói của bạn. Tôi giả định rằng package1package2là, theo một cách, một phần của gói tương tự, nhưng để có thể đọc mà bạn đang tách những thành nhiều file.

Nếu trường hợp trước đó là của bạn, bạn có thể sử dụng cùng tên gói vào các tệp đó và nó sẽ giống như nếu có cùng một tệp.

Đây là một ví dụ:

thêm

package math

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

trừ đi

package math

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

donoth.go

package math

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

Tôi không phải là chuyên gia về Go và đây là bài viết đầu tiên của tôi trong StackOveflow, vì vậy nếu bạn có một số lời khuyên, nó sẽ được đón nhận.


23

Tôi có một vấn đề tương tự và giải pháp tôi hiện đang sử dụng sử dụng các mô đun Go 1.11. Tôi có cấu trúc như sau

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

Và tôi có thể nhập gói1 và gói2 từ project1 và project2 bằng cách sử dụng

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

Sau khi chạy go mod init projects. Tôi có thể sử dụng go buildtừ thư mục project1 và project2 hoặc tôi có thể làm go build -o project1/exe project1/*.gotừ thư mục dự án.

Nhược điểm của phương pháp này là tất cả các dự án của bạn cuối cùng đều chia sẻ cùng một danh sách phụ thuộc trong go.mod. Tôi vẫn đang tìm kiếm một giải pháp cho vấn đề này, nhưng có vẻ như nó có thể là cơ bản.


9

Kể từ khi giới thiệu go.mod , tôi nghĩ rằng việc quản lý gói nội bộ và bên ngoài trở nên dễ dàng hơn. Sử dụng go.mod , có thể có dự án đi bên ngoài GOPATH.

Nhập gói nội địa:

Tạo một thư mục demoproject và chạy lệnh sau để tạo tệp go.mod

go mod init demoproject

Tôi có một cấu trúc dự án như bên dưới trong thư mục demoproject .

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

Đối với mục đích demo, chèn đoạn mã sau vào tệp model.go .

package model

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

Trong main.go , tôi đã nhập mô hình Nhân viên bằng cách tham khảo "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)
}

Nhập phụ thuộc bên ngoài:

Chỉ cần chạy go getlệnh bên trong thư mục dự án.

Ví dụ:

go get -u google.golang.org/grpc

Nó nên bao gồm phụ thuộc mô-đun trong tệp 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/USE-go-modules


can't load package: package .: no Go files in...(đi xây dựng trong thư mục của go.mod)
Sebi2020

Một lệnh cấm như vậy nhưng tôi đã mất một khoảng thời gian đáng xấu hổ để tìm câu trả lời và bài đăng của bạn là dễ đọc và hữu ích nhất. Cảm ơn bạn!
Harold Cavendish

8

Để thêm gói "cục bộ" vào dự án của bạn, hãy thêm một thư mục (ví dụ "gói_name"). Và đặt các tập tin thực hiện của bạn trong thư mục đó.

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

Trong bạn package mainlàm điều này:

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

Trong trường hợp package_namelà tên thư mục và nó phải phù hợp với tên gói sử dụng trong các tập tin whatever_name1.go và whatever_name2.go. Nói cách khác, tất cả các tệp có thư mục con phải cùng gói.

Bạn có thể lồng thêm các thư mục con miễn là bạn chỉ định toàn bộ đường dẫn đến thư mục mẹ trong quá trình nhập.


2
Đây là một đề xuất tốt, ngoại trừ trong bất kỳ kernel nào hoảng loạn, dấu vết ngăn xếp được đổ từ nhị phân cho thấy đường dẫn github.com chẳng hạn, không phải luôn luôn là hành vi mong muốn nhất. Có những lá cờ để ngăn chặn điều này, nhưng không cần thiết chỉ để đạt được tổ chức gói đơn giản và đôi khi tôi thấy rằng nó không thành công.
Kenny Powers

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

3

Bạn có thể dùng 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()
}

thanh / go.mod

module github.com/my/bar

go 1.14

thanh / fn.go

package github.com/my/bar

import "fmt"

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

Nhập một gói nội bộ cũng giống như nhập một pacakge bên ngoài

ngoại trừ bên trong tệp go.mod, bạn thay thế tên gói bên ngoài đó bằng một thư mục cục bộ.

Đường dẫn đến thư mục có thể đầy đủ hoặc tương đối "/ path / to / bar" hoặc "../bar"

https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive https://thewebivore.com/USE-replace-in-go-mod-to-point -to-your-local-module /

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.