Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the version detection script from vibe-d:tls to deimos/openssl #66

Merged
merged 6 commits into from
May 21, 2022

Conversation

Geod24
Copy link
Collaborator

@Geod24 Geod24 commented May 16, 2022

The OpenSSL bindings are often annoying to deal with, as different versions will expose slightly different API, which will then fail to link, and there was no good way to detect the version in use. Over the year, Vibe.d has built a script to solve this problem, which relies firstly on pkg-config, then on the openssl binary itself.

While there could probably be issues with non-conventional setups, using pkg-config seems like the best way forward. However, even if vibe-d:tls does the detection, the bindings in here still assume 1.1.0. Moving the detection to this repository will hopefully reduce the need for duplicated work in downstream users of openssl.

Currently, the importPath is the root directly,
meaning that it's impossible to store D source files that
are not directly part of the library (such as a D script).
Moving to a source directory will allow us to add the vibe.d
version detection script here.
@Geod24
Copy link
Collaborator Author

Geod24 commented May 16, 2022

@CyberShadow : I suppose you will be the most affected by this, given the directory structure change, but without a dedicated source path any script would end up polluting the library.

@Geod24
Copy link
Collaborator Author

Geod24 commented May 18, 2022

CC @schveiguy

@schveiguy
Copy link
Contributor

eh? I haven't had much issues with ssl. I mostly disable it and rely on the wrapper web server (apache) so far.

@CyberShadow
Copy link
Member

With this change, is it possible to still use the bindings as a submodule, without Dub?

@Geod24
Copy link
Collaborator Author

Geod24 commented May 18, 2022

@CyberShadow : Yes. You would have to:

  1. Change your build system's import path to take source into account;
  2. Call generate_version.d in your build system;

Point 2 will not work well on Windows, so the bindings will always assume 1.1.0. But that's no worse from what we have currently, can be overridden by the user, or fixed at the binding's level.

@Geod24
Copy link
Collaborator Author

Geod24 commented May 18, 2022

@schveiguy : Just saw you in the list of "maintainers" (as in, you did a release).
The issue I'm seeing is that, using Ubuntu 22.04, OpenSSL is at v3.0.x.
Because Vibe.d depends on v1.x.x of this repo, any other library that depend on openssl must implement the same hacks as vibe-d:tls.

@schveiguy
Copy link
Contributor

If I did a release, I probably messed it up lol.

@CyberShadow is the real expert here.

@CyberShadow
Copy link
Member

Call generate_version.d in your build system;

This isn't going to work, because I don't think rdmd or similar programs (rund, dmd -i) have such a facility.

What other options are there? Can we pregenerate this file for applicable versions, and/or use version blocks?

@Geod24
Copy link
Collaborator Author

Geod24 commented May 18, 2022

I don't think rdmd or similar programs (rund, dmd -i) have such a facility.

I'm not sure what you mean ? The code depends solely on stdlib so doing dmd -i or rdmd will work.
The only reason we were using dub instead of rdmd is because of a weird temp folder error (vibe-d/vibe.d@2512c7f) which might only happen with dub + rdmd (as it was still called from a preGenerateCommand or could have been fixed since.

Regarding other approaches, we could have version (OpenSSL110) { ... } else version (OpenSSL100) else import version_file;. How does it sound ?

@CyberShadow
Copy link
Member

CyberShadow commented May 19, 2022

I'm not sure what you mean ? The code depends solely on stdlib so doing dmd -i or rdmd will work.

Sorry, I meant that they don't have any hooks for running an external program before beginning the build. I understood this as that all programs which use these bindings but don't use Dub will need to add or change their build scripts to invoke this generator program, right?

Regarding other approaches, we could have version (OpenSSL110) { ... } else version (OpenSSL100) else import version_file;. How does it sound ?

That sounds like the right direction, but I would go even a little further.

  • In the absence of other information, assume that we are targeting the latest version of OpenSSL supported by the bindings.
  • If a version exists which specifies the OpenSSL version to target, use the information from there.
  • If a version exists which indicates that we are using a build system capable of running external commands as part of the build (maybe OpenSSLDetectVersion), then import version_file. I think this is a little better because all such build systems will also make it easy to specify an additional version to allow detecting this circumstance.

What do you think?

@Geod24
Copy link
Collaborator Author

Geod24 commented May 19, 2022

Sorry, I meant that they don't have any hooks for running an external program before beginning the build. I understood this as that all programs which use these bindings but don't use Dub will need to add or change their build scripts to invoke this generator program, right?

Correct. To be fair, if they don't have such hooks, they aren't much of a build system. But that doesn't mean we should break things for those, obviously.

In the absence of other information, assume that we are targeting the latest version of OpenSSL supported by the bindings.

That makes sense, but at this point, it would be a breaking change. Could we do this in the next major ? Unless you plan to release a major anyway because of the files having moved ?

If a version exists which specifies the OpenSSL version to target, use the information from there.

Question is, should we support every version ? I suggest to only support those listed in the "Table of content" of the changelog. In practice, it means that we'll have DeimosOpenSSL_1_1_0 meaning 1.1.0l, and not DeimosOpenSSL_1_1_0l, DeimosOpenSSL_1_1_0k, etc...

What do you think?

Sounds good to me.

@Geod24
Copy link
Collaborator Author

Geod24 commented May 19, 2022

Updated

@CyberShadow
Copy link
Member

Correct. To be fair, if they don't have such hooks, they aren't much of a build system. But that doesn't mean we should break things for those, obviously.

Right, "system" is not a good term no describe these. More like method of building sufficiently-simple D software. SSL is such an ubiquitous part of software today though that we should probably aim to keep things as simple as we can.

That makes sense, but at this point, it would be a breaking change. Could we do this in the next major ? Unless you plan to release a major anyway because of the files having moved ?

Yeah, I think this warrants a major version bump.

Question is, should we support every version ? I suggest to only support those listed in the "Table of content" of the changelog. In practice, it means that we'll have DeimosOpenSSL_1_1_0 meaning 1.1.0l, and not DeimosOpenSSL_1_1_0l, DeimosOpenSSL_1_1_0k, etc...

We technically don't have the resources to actively support even one version, all support is best-effort and user-contributed. I understand that by "support" you mean "keep it in the tree", which to me raises another question - why do we aim to support multiple OpenSSL versions with one bindings version, anyway, instead of using branches? With branches we could essentially "support" any OpenSSL version indefinitely, and users can continue contributing fixes to that OpenSSL version's branch. I can see how Dub's treatment of version tags could make things a little unobvious but it doesn't seem insurmountable.

Furthermore, I'm not even sure if it makes sense to define a backwards-compatibility policy based on OpenSSL version numbers. AFAIK, OpenSSL versioning is based on compatibility with C source code, but what we really care about is compatibility with OpenSSL's ABI, which to OpenSSL developers is an implementation detail.

On a more practical note, I think we can drop code for older OpenSSL ABIs along with major version bumps of this package, and if someone would like to contribute fixes for an old ABI, we can always revive the last commit before the removal as a new branch and create tags from there.

@CyberShadow
Copy link
Member

CyberShadow commented May 19, 2022

why do we aim to support multiple OpenSSL versions with one bindings version, anyway, instead of using branches?

After posting I realized that one reason in favor doing so is exactly what this pull request aims to facilitate - targeting varying OpenSSL ABI versions with one Dub configuration file. I don't think Dub has any facilities to calculate a dependency version dynamically based on an external condition, so this is as good as it gets there. Same goes with Git submodules.

@Geod24
Copy link
Collaborator Author

Geod24 commented May 19, 2022

why do we aim to support multiple OpenSSL versions with one bindings version, anyway, instead of using branches?

I work on Ubuntu 22.04, my coworkers work on 18.04 or 20.04. The only way we can work together is to use * as version and not commit dub.selections.json. Even then, using * means the people on 18.04 will fetch the newer versions of the bindings.

AFAIK, OpenSSL versioning is based on compatibility with C source code, but what we really care about is compatibility with OpenSSL's ABI, which to OpenSSL developers is an implementation detail.

Yes and no. They have taken a quite flawed approach to compatibility, but they have to maintain forward compatibility with patch versions (what they call build), otherwise shared library linking would not work. I can imagine there might be exceptions (e.g. if we used some private / undocumented API), but overall the public API have to be stable across "build" of the same "version".

@Geod24
Copy link
Collaborator Author

Geod24 commented May 19, 2022

We technically don't have the resources to actively support even one version

Agreed, this is more of a "best effort" basis. For reference, Vibe.d does this ATM:
https://github.com/vibe-d/vibe.d/blob/d91ee15d75a9644ad7dabe8de38db805d83af5d3/tls/vibe/stream/openssl.d#L61-L229

I simply want to encourage people to send their fixes upstream instead of hacking it downstream.

Copy link
Member

@CyberShadow CyberShadow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions:

  • Is this going to interfere with caching? Such as forcing a rebuild every time?
  • What happens if the version of OpenSSL on the build machine is updated? If the file is generated every time, no problem, but otherwise can we make it clearer which file the user needs to delete?

Currently, the importPath is the root directly,
meaning that it's impossible to store D source files that
are not directly part of the library (such as a D script).
Moving to a source directory will allow us to add the vibe.d
version detection script here.

I think in theory this should be achievable by setting sourcePaths (glob these files) to deimos and importPaths (tell the compiler to look here for imports) to ., but I don't know if Dub actually implements this distinction sufficiently meaningfully to allow this.

LGTM otherwise though haven't tried building something with this yet.

.gitignore Outdated Show resolved Hide resolved
.gitignore Outdated Show resolved Hide resolved
scripts/generate_version.d Outdated Show resolved Hide resolved
@@ -19,4 +26,6 @@ configuration "unittest" {
targetType "executable"
dflags "-main"
excludedSourceFiles "source/deimos/openssl/applink.d"
preGenerateCommands `${DUB} scripts/generate_version.d` platform="posix"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we hoping for a Windows developer to come along and fix the script (maybe make it check the DLL version in PATH, or the typical install location)?

I have this function for another project which attempts to find an OpenSSL installation:

string findOpenSsl()
{
	auto paths = environment["PATH"]
		.split(pathSeparator)
		.chain(
			cartesianProduct(
				only(
					``, // current drive root
					environment.get("SystemDrive", "c") ~ ":",
					environment.get("ProgramFiles", `C:\Program Files`),
					environment.get("ProgramFiles(x86)", `C:\Program Files (x86)`),
					environment.get("ProgramW6432", `C:\Program Files (x86)`),
				),
				only(
					`\OpenSSL-Win32\bin\`,
					`\OpenSSL-Win64\bin\`,
				),
			)
			.map!(t => t[0] ~ t[1])
		)
		.chain(environment.get("OPENSSL_CONF").dirName.only)
		.chain(opensslInstallDir.buildPath("bin").only)
		.array;
	auto openssl = findExecutable("openssl", paths);
	debug writeln("Detected OpenSSL in: ", openssl);
	return openssl;
}

string opensslInstallDir()
{
	version (Windows)
	{
		auto key = Registry
			.localMachine
			.getKey(`SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall`);
		foreach (name; key.keyNames)
			if (name.startsWith("OpenSSL"))
				return key
					.getKey(name)
					.getValue("InstallLocation")
					.value_SZ;
	}
	return null;
}

Copy link
Collaborator Author

@Geod24 Geod24 May 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's exactly what I was hoping for. Hoping you'll build on it once it's pushed. But even if no one builds on it, the change is still a net improvement IMO.

source/deimos/openssl/opensslv.d Outdated Show resolved Hide resolved
source/deimos/openssl/opensslv.d Show resolved Hide resolved
source/deimos/openssl/opensslv.d Outdated Show resolved Hide resolved
source/deimos/openssl/opensslv.d Outdated Show resolved Hide resolved
Comment on lines 140 to 142
enum OPENSSL_VERSION_NUMBER =
OPENSSL_MAKE_VERSION(OPENSSL_VERSION_MAJOR, OPENSSL_VERSION_MINOR, OPENSSL_VERSION_PATCH, OPENSSL_VERSION_BUILD);
OPENSSL_MAKE_VERSION(OpenSSLVersion.Major, OpenSSLVersion.Minor,
OpenSSLVersion.Patch, OpenSSLVersion.Build);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems especially odd that this will be kept up to date, but not the individual constants. If those were to be changed to point to the struct template, then this change becomes unnecessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. I decided to update this because OpenSSL has this constant has a way to version code. With our D bindings, I don't think that this is really useful TBH.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what you mean by "OpenSSL has this constant has a way to version code".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a way*

I meant that those were designed to work with C code and a preprocessor. Macros only make sense in a binding when they are used to alias an API. A better way to do what OPENSSL_VERSION_NUMBER does in D would be to implement an opCmp so that you could compare two versions.

But anyway, as it's the only point of contention, and I don't use those defs myself, I'll just make them alias the struct fields.

@CyberShadow
Copy link
Member

Agreed, this is more of a "best effort" basis. For reference, Vibe.d does this ATM:

Right, ae has a block like that too.

@Geod24
Copy link
Collaborator Author

Geod24 commented May 19, 2022

Is this going to interfere with caching? Such as forcing a rebuild every time?
What happens if the version of OpenSSL on the build machine is updated? If the file is generated every time, no problem, but otherwise can we make it clearer which file the user needs to delete?

The version file is generated every time, but is only written if it is different from what's on disk. So it should not interfere with build systems that take file timestamps into account, and will work if the version is updated (be it upgrade or downgrade).
It would also show up if for some reason the file ended up in CI. See vibe-d/vibe.d@771246a

@Geod24
Copy link
Collaborator Author

Geod24 commented May 19, 2022

Marked as resolved the thing that were addressed, commented otherwise, and pushed an updated version.

EDIT: And added a comment about the file writing logic.

This script was originally written for vibe-d:tls module.
However, it makes more sense to have the detection done in bindings.
Before this commit, the OpenSSL version was just assumed to be 1.1.0h.
@Geod24
Copy link
Collaborator Author

Geod24 commented May 20, 2022

Updated:

  • Removed explicit v3.1 branch;
  • Removed deprecations on OPENSSL_VERSION_* and made them point to the struct fields;
  • Removed OPENSSL_VERSION_TEXT as it includes the release date and doesn't seem generally useful;

Copy link
Member

@CyberShadow CyberShadow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested building DFeed against it and it built/linked fine, just needed to update the import path symlink (lib/deimos/openssl from ../deimos-openssl/deimos/openssl to ../deimos-openssl/source/deimos/openssl).

I didn't test the Dub side but I'm guessing you have that covered.

@CyberShadow
Copy link
Member

Since we aim to provide compatibility with more than one OpenSSL version, I'm thinking the next tag should be just v3.0.0 without any suffix, what do you think?

@Geod24
Copy link
Collaborator Author

Geod24 commented May 20, 2022

That makes sense. And the README could probably use an update ?

@CyberShadow CyberShadow merged commit ce9fd3f into D-Programming-Deimos:master May 21, 2022
@CyberShadow
Copy link
Member

@Geod24 Geod24 deleted the openssl-3 branch November 20, 2022 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants