I am compiling and building an example program using rust. I chose rustc instead of cargo for compiling because it being a simple personal test project. So far using rustc for compiling and building executable worked fine but when I tried to add an external rand package its giving me this error
1 | extern crate rand;
| ^^^^^^^^^^^^^^^^^^ can't find crate
This is the full code
extern crate rand;
use rand::Rng;
fn main() {
for x in 1..11 {
let random_number = rand::thread_rng()
.gen_range(1, 101);
println!("{} -> {}", x, random_number)
}
}
How can I add external packages and run with rustc?
This is possible without Cargo, but you'll have to do what it normally does for you.
Download all the dependencies.
Compile all the dependencies with rustc using the correct flags.
rand v0.7.3
├── getrandom v0.1.14
│ ├── cfg-if v0.1.10
│ └── libc v0.2.66
├── libc v0.2.66 (*)
├── rand_chacha v0.2.1
│ ├── c2-chacha v0.2.3
│ │ └── ppv-lite86 v0.2.6
│ └── rand_core v0.5.1
│ └── getrandom v0.1.14 (*)
└── rand_core v0.5.1 (*)
rand isn't too bad, with only 8 transitive dependencies (including rand itself, not including duplicates). Still, you'll have to go to crates.io or github and download the correct version of the source for each.
Then comes compiling. The minimum you'll have to do to compile your own binary is rustc -L dependency=/path/to/dependency/dir src/main.rs. But remember that you have to do this for each of the 8 dependencies, and all of those have their own external dependencies. You'll also need to figure out the right order to compile them.
Moveover, some crates have their own settings in their Cargo.toml that have to be respected. Some crates even have a build script that needs to be compiled and run (libc is an example in this dependency tree).
Alternatively, you could just put
[dependencies]
rand = "0.7.3"
in your Cargo.toml and run cargo build. Your choice. Cargo is one of the nicest things about Rust, so I suggest you use it.
P.S. To see what exactly cargo is doing, run cargo clean to remove any already compiled dependencies. Then run cargo build --verbose (or cargo build -vv if you're brave). You'll see all the flags that get passed to rustc, scripts that get run and everything else.
I wanted to expand on #SCappella's answer. I would encourage you to use cargo as well. If you are familiar with package managers from other languages such as JS, PHP, or Python, you might be interested in cargo edit.
It allows you to run commands cargo add rand (latest version), cargo add rand#0.7.3 (specific version), cargo upgrade rand (upgrade only rand), cargo upgrade (upgrade all dependencies) in CLI instead of editing Cargo.toml file directly.
To install it, run cargo install cargo-edit, and then read the documentation on the website on how you can use it.
Related
I have a monorepo setup for my go project. I would love it if I could find a way to use go build (or similar internal tool) to get a list of targets that need to be re-built.
Here is an example of what I am looking for:
...
├── pkg //shared code across mono repo
│ └── math
│ └── common_operations.go
└── cmd // individual packages to be built
├── package1
│ └── main.go
└── package2
└── main.go
The package1 program calls a subtract function from the math shared library. The package2 program calls an add function.
If I change the package1 code, only the package1 target is listed
If I change the package2 code, only the package2 target is listed
If I change the add function in the shared library, only the package2 target is listed
If I change the subtract function in the shared library, only the package1 target is listed
If I change all the functions in the shared library, both package1 and package2 rebuilds.
I would be perfectly happy to use use the internal build package and get the list programatically. I am just am unfamiliar with it.
What I have tried:
Bazel has an option for this but I would prefer to avoid using bazel if at all possible.
the bazel command: bazel build cmd/some-target --check_up_to_date returns error code 0 if it is up to date, otherwise it returns error code 1.
Which is technically a solution, but my need, as you might have inferred, is ci/cd based. And I want to avoid integrating Bazel into that process as much as possible.
Not really sure of the use case here, are you OK with actually compiling the packages as well? In that case maybe go build -v can do the job for you. From go help build:
-v
print the names of packages as they are compiled.
My desire was to find something similar to a go build option that would basically spit out a true or false if a target package was up-to-date. I would be happy if someone out there found a tool ( Earthly I am looking at you right now) that could solve this.
The closest thing to a solution I could find to solve my issue was to run this command:
go build -n cmd/some-target
And if the output is:
touch some-target
Then, it must be up-to-date. If the output is a long list of commands, then it isn't.
If it is not up-to-date. You could then get the package name using:
go build -v
To get the name of the package and move it to the next stage of the CI process (building target, testing target, building image, etc).
Obviously it is a little hacky and requires self-rolling a solution and specifics would likely need to be changed on your exact needs. It also requires, as #zacho314 mentioned, saving the state of the go build cache, but most modern CI technologies have a solution for that. I am sure I will do something similar to this for now.
TL;DR: Where do .so files end up when you install plugins with go install -buildmode=plugin?
I have a project that uses plugins. The layout is something like this:
myproject/
├── main.go
└── modules
├── bar
│ └── main.go
└── foo
└── main.go
When I run go install the binary gets installed OK.
But I would also like to run go install for each of my modules and have them available to the main binary everywhere on the system.
If I run go install -buildmode=plugin from inside a module folder (say, modules/foo) the command runs to completion but I can't find the resulting file anywhere.
Installing a normal package ends up in:
GOPATH/pkg/<goos>_<goarch>_dynlink/path/to/parent/folder/packagename.a
Installing main packages end up in:
GOPATH/bin/foldername
(where foldername is the parent folder of the main package you install, it'll get an .exe extension on windows).
When you "go install" a plugin (using -buildmode=plugin), that ends up in
GOPATH/pkg/<goos>_<goarch>_dynlink/path/to/parent/folder/foldername.a
I have this folder structure for my fib package:
$ tree
.
└── src
└── fib
├── fib
│ └── main.go
├── fib.go
└── fib_test.go
(main.go is in package main, fib(_test).go is in package fib)
GOPATH is set to $PWD/src, GOBIN is set to $PWD/bin. When I run go install fib/fib, I get a file called fib in the directory bin (this is what I expect):
$ tree bin/
bin/
└── fib
But when I set GOOS or GOARCH, the directory in the form GOOS_GOARCH is created:
$ GOARCH=386 GOOS=windows go install fib/fib
$ tree bin/
bin/
└── windows_386
└── fib.exe
This is not what I want. I'd like to have the file fib.exe in the bin directory, not in the sub directory bin/windows_386.
(How) is this possible?
That doesn't seem possible, as illustrated in issue 6201.
GOARCH sets the kind of binary to build.
You might be cross-compiling: GOARCH might be arm.
You definitely don't want to run the arm tool on an x86 system.
The host system type is GOHOSTARCH.
To install the api tool (or any tools) you need to use
GOARCH=$(go env GOHOSTARCH) go install .../api
and then plain 'go tool' will find them.
In any case (GOARCH or GOHOSTARCH), the go command will install in a fixed location that you cannot change.
The phrase "I (don't) want" is incompatible with the go tool; the go tool works how it works. You can a) copy the file to where you want it to be after installing it with the go tool or b) compile it yourself, e.g. by invoking 6g manually (here you can specify the output). If you are unhappy with how the go tool works, just switch to a build tool of your liking, e.g. plain old Makefiles. Note that the go tool helps you there too, e.g. by invoking the compiler via go tool 6g
When I build a Go binary, I usually do something like this:
go build -ldflags "-X main.basedir somevar" -o mybuilddir/bin/myfile mypackage/main"
this builds the binary and places it in a custom directory. But this doesn't keep the "intermediate" package files beneath pkg/, which would speed up the next compilation runs.
The solution would be go install, but I cannot specify an output directory. It seems to be possible to set the binary directory with GOBIN, but I am unable to specify the name of the executable (always main).
What is a possible solution to this problem?
Custom destionation directory
Custom name (not main)
Keep intermediate generated package files (.a)
This is the src directory of GOPATH:
GOPATH/src$ tree
.
└── mypackage
├── packagea
│ └── packagea.go
├── packageb
│ └── packageb.go
└── main
└── mypackage.go
With go install, the package files (.a) are created in $GOPATH/pkg, with go build, I can't find the .a files anywhere.
Update Nov. 2017: Go 1.10 (Q1 2018) will add caching for go build and go install: see "go build rebuilds unnecessarily".
Original answer (2014)
With go install, the package files (.a) are created in $GOPATH/pkg, with go build, I can't find the .a files anywhere.
As mentioned in "How does the go build command work ?"
The final step is to pack the object file into an archive file, .a, which the linker and the compiler consume.
Because we invoked go build on a package, the result is discarded as $WORK is deleted after the build completes.
If we invoke go install -x two additional lines appear in the output
mkdir -p /home/dfc/go/pkg/linux_arm/crypto/
cp $WORK/crypto/hmac.a /home/dfc/go/pkg/linux_arm/crypto/hmac.a
This demonstrates the difference between go build and install;
build builds,
install builds then installs the result to be used by other builds.
I have an existing 2000 LOC perl script with a Tkx GUI that I just inherited on my first day as an intern at a place where I am the sole programmer (everyone else is an IC engineer, but they do their simulations in perl).
The goal is to produce an executable for clients to run without having to install perl nor anything else. Apparently this has been possible in the past.
I've only been able to get the Program to build by installing ActiveTcl 8.5.15, ActivePerl 5.16, installing via PPM Win32::API and Win32::Exe, tk, tkx, Carp, then installing via cpanp i PAR::Packer. This very specific mix is the only one I found that produced any results that worked.
Then it's:
pp -vvv -l C:\Perl\lib\auto\Tcl\tkkit.dll -l C:\Perl\lib\auto\Tcl\Tcl.dll -l C:\Perl\lib\auto\Win32\API\API.dll --gui -o .\<THE NAME OF THE FILE.EXE> .\<PERL SOURCE>.pl
From here I get a working executable, except it will not run on any of the other machines I've tested it on. It's not an arch issue as far as I can tell.
The documentation by the previous developer is extremely lacking on the subject (he's better on documenting the actual code). I'm told he migrated it to StrawberryPerl then back to ActivePerl when that broke, but as yet it's still pretty broken over here and the existing build environment is long gone.
Any help would be appreciated.
Action: Use PAR::Packer pp to compile Windows binary from a perl script so that clients can run it without perl
Expected Result: A tk GUI Window opens and stays open.
Actual Result: A tk GUI Window does not open, no errors are produced on console nor in any prompt.
Thanks to all who responded.
I actually found the solution myself, which was that this block
BEGIN { if (exists $ENV{PAR_PROGNAME}) { use Config (); $ENV{PERL_TCL_DL_PATH} = catfile($ENV{PAR_TEMP}, 'tkkit.' . $Config::Config{dlext}, ); } }
needs to appear before a "use Tkx;" to properly instruct PAR::Packer to grab the necessary parts of Tcl to package the program. So the necessary libraries were being left out and the executable was searching for ActiveTcl installations in %path% that did not exist. But thank you for the detail about the additional module for debugging.
This is my solution on Windows 7 with strawberry perl 5.26.1 and tkx 1.09:
pp --compile --noscan --dependent --compress 6 hello.pl
Copy generated .exe and needed dlls to a directory, and rename this directory to bin
Copy tcl and tk library to lib directory on the same hierarchy as bin
Now my generated file structure:
$ tree -L 2
.
├── bin
│ ├── hello.exe
│ ├── hello.pl
│ ├── libgcc_s_dw2-1.dll
│ ├── libstdc++-6.dll
│ ├── libwinpthread-1.dll
│ ├── perl526.dll
│ ├── tcl86.dll
│ ├── tk86.dll
│ └── zlib1.dll
└── lib
├── tcl8.6
└── tk8.6
BTW, the whole folder can be compressed to about 2.7MB by 7-zip on my machine.