Skip to content

Commit

Permalink
8283707: Support <major.minor.update.build> version format on Windows
Browse files Browse the repository at this point in the history
Reviewed-by: almatvee
  • Loading branch information
Alexey Semenyuk committed Jul 19, 2022
1 parent 96a542f commit 977e094
Show file tree
Hide file tree
Showing 15 changed files with 1,298 additions and 60 deletions.
9 changes: 7 additions & 2 deletions make/modules/jdk.jpackage/Lib.gmk
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,23 @@ ifeq ($(call isTargetOs, windows), true)

JPACKAGE_TARGETS += $(BUILD_LIB_JPACKAGE)

JPACKAGE_WIXHELPER_SRC := \
$(call FindSrcDirsForComponent, jdk.jpackage, libwixhelper) \
$(call FindSrcDirsForComponent, jdk.jpackage, common)

# Build Wix custom action helper
# Output library in resources dir, and symbols in the object dir
$(eval $(call SetupJdkLibrary, BUILD_LIB_WIXHELPER, \
NAME := wixhelper, \
OUTPUT_DIR := $(JPACKAGE_OUTPUT_DIR), \
SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libwixhelper, \
OPTIMIZATION := LOW, \
SRC := $(JPACKAGE_WIXHELPER_SRC), \
CXXFLAGS := $(call JpackageWithStaticCrt, $(CXXFLAGS_JDKLIB)) \
$(JPACKAGE_CXXFLAGS_windows), \
$(addprefix -I, $(JPACKAGE_WIXHELPER_SRC)) $(JPACKAGE_CXXFLAGS_windows), \
LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK), \
LIBS := $(LIBCXX), \
LIBS_windows := msi.lib Shlwapi.lib User32.lib, \
LIBS_windows := User32.lib, \
))

JPACKAGE_TARGETS += $(BUILD_LIB_WIXHELPER)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -36,7 +36,6 @@
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
Expand Down Expand Up @@ -105,7 +104,7 @@ void configureWixPipeline(WixPipeline wixPipeline) {
}

// Only needed if we using CA dll, so Wix can find it
if (withInstallDirChooserDlg) {
if (withCustomActionsDll) {
wixPipeline.addLightOptions("-b",
getConfigRoot().toAbsolutePath().toString());
}
Expand All @@ -119,7 +118,7 @@ void configureWixPipeline(WixPipeline wixPipeline) {
void addFilesToConfigRoot() throws IOException {
super.addFilesToConfigRoot();

if (withInstallDirChooserDlg) {
if (withCustomActionsDll) {
String fname = "wixhelper.dll"; // CA dll
try (InputStream is = OverridableResource.readDefault(fname)) {
Files.copy(is, getConfigRoot().resolve(fname));
Expand Down Expand Up @@ -481,6 +480,7 @@ void addToWixPipeline(WixPipeline wixPipeline) {
private boolean withInstallDirChooserDlg;
private boolean withShortcutPromptDlg;
private boolean withLicenseDlg;
private boolean withCustomActionsDll = true;
private List<CustomDialog> customDialogs;

private static final BundlerParamInfo<Boolean> INSTALLDIR_CHOOSER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@

<Fragment>

<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
<CustomAction Id="JpCheckInstallDir" BinaryKey="JpCaDll" DllEntry="CheckInstallDir" />

<UI>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
<?endif?>

<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>

<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />

<!-- Standard required root -->
<Directory Id="TARGETDIR" Name="SourceDir"/>

Expand Down Expand Up @@ -114,13 +118,18 @@
<?endif?>

<?ifndef JpAllowUpgrades ?>
<Custom Action="JpDisallowUpgrade" After="FindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
<Custom Action="JpDisallowUpgrade" After="JpFindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
<?endif?>
<?ifndef JpAllowDowngrades ?>
<Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
<Custom Action="JpDisallowDowngrade" After="JpFindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
<?endif?>
<RemoveExistingProducts Before="CostInitialize"/>
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
</InstallExecuteSequence>

<InstallUISequence>
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
</InstallUISequence>

</Product>
</Wix>
233 changes: 233 additions & 0 deletions src/jdk.jpackage/windows/native/common/MsiCA.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

#include "MsiCA.h"
#include "MsiDb.h"
#include "MsiUtils.h"
#include "FileUtils.h"
#include "ErrorHandling.h"
#include "Toolbox.h"


#pragma comment(lib, "msi.lib")


namespace msi {

tstring CAImpl::getProperty(const tstring& name) const {
return getPropertyFromCustomAction(handle, name);
}


void CAImpl::setProperty(const tstring& name, const tstring& value) {
if (value.empty()) {
JP_THROW(tstrings::any() << "Attempt to assign empty value to '"
<< name << "' MSI property");
}

LOG_TRACE(tstrings::any() << "Setting MSI property '" << name <<
"' to '" << value << "'");
const UINT status = MsiSetProperty(handle, name.c_str(), value.c_str());
if (status != ERROR_SUCCESS) {
JP_THROW(msi::Error(tstrings::any() << "MsiSetProperty(" << name
<< ", " << value << ") failed", status));
}
}


void CAImpl::removeProperty(const tstring& name) {
LOG_TRACE(tstrings::any() << "Removing MSI property '" << name << "'");
const UINT status = MsiSetProperty(handle, name.c_str(), NULL);
if (status != ERROR_SUCCESS) {
JP_THROW(msi::Error(tstrings::any() << "MsiSetProperty(" << name
<< ", NULL) failed", status));
}
}


Guid CAFacade::getProductCode() const {
return impl.getProperty(_T("ProductCode"));
}


bool CAFacade::isInMode(MSIRUNMODE v) const {
return MsiGetMode(impl.getHandle(), v) != FALSE;
}


tstring CAFacade::getModes() const {
tstring modes;
// Iterate all modes in the range [MSIRUNMODE_ADMIN, MSIRUNMODE_COMMIT]
for (int mode = MSIRUNMODE_ADMIN; mode != MSIRUNMODE_COMMIT + 1; ++mode) {
modes.insert(modes.end(), isInMode(MSIRUNMODE(mode)) ?
_T('1') : _T('0'));
}
return modes;
}


void CAFacade::doAction(const tstring& name) const {
const UINT status = MsiDoAction(impl.getHandle(), name.c_str());
if (status != ERROR_SUCCESS) {
JP_THROW(msi::Error(tstrings::any() << "MsiDoAction(" << name
<< ") failed", status));
}
}


tstring CAFacade::normalizeDirectoryPath(tstring v) {
if (v.empty()) {
return v;
}
std::replace(v.begin(), v.end(), '/', '\\');
return FileUtils::removeTrailingSlash(v) + _T("\\");
}


CA& CA::setPropertyIfEmpty(const tstring& name, const tstring& v) {
if (getProperty(name).empty()) {
setProperty(name, v);
}
return *this;
}


tstring DeferredCA::getArg() const {
if (isInMode(MSIRUNMODE_SCHEDULED) || caArgPropertyName.empty()) {
// Details on accessing MSI properties from deferred custom actions:
// http://blogs.technet.com/b/alexshev/archive/2008/03/25/property-does-not-exist-or-empty-when-accessed-from-deferred-custom-action.aspx
// http://stackoverflow.com/questions/17988392/unable-to-fetch-the-install-location-property-in-a-deferred-custom-action
// http://stackoverflow.com/questions/11233267/how-to-pass-customactiondata-to-a-customaction-using-wix
return impl.getProperty(_T("CustomActionData"));
}

return impl.getProperty(caArgPropertyName);
}


tstring DeferredCA::getParsedArg(const tstring& name) const {
const auto entry = theParsedArgs.find(name);
if (entry == theParsedArgs.end()) {
JP_THROW(tstrings::any() << "Argument << '" << name
<< "' not found.");
}
return entry->second;
}


namespace {
std::pair<tstring, tstring> parseArg(const tstring& v) {
const auto pos = v.find(_T('='));
if (pos == tstring::npos) {
JP_THROW(tstrings::any() << "Missing expected '=' character in ["
<< v << "] string.");
}
return std::pair<tstring, tstring>(v.substr(0, pos), v.substr(pos + 1));
}

void parseArgsImpl(DeferredCA::ArgsCtnr& dst, const tstring& src) {
const tstring_array pairs = tstrings::split(src, _T("*"));
for(auto it = pairs.begin(), end = pairs.end(); it != end; ++it) {
const auto pair = parseArg(*it);
dst[pair.first] = pair.second;
}
}
} // namespace
void DeferredCA::parseArgs(ArgsCtnr& dst, const tstring& src) {
DeferredCA::ArgsCtnr tmp;

const auto end = src.find(_T("**"));
if (end != tstring::npos) {
parseArgsImpl(tmp, src.substr(0, end));
tmp[tstring()] = src.substr(end + 2);
} else {
parseArgsImpl(tmp, src);
}

tmp.insert(dst.begin(), dst.end());
tmp.swap(dst);
}


MsiLogAppender::MsiLogAppender(MSIHANDLE h): handle(h),
ctorThread(GetCurrentThreadId()) {

}

void MsiLogAppender::append(const LogEvent& v) {
const LPCTSTR format = _T("[%02u:%02u:%02u.%03u%s%s:%u (%s)] %s: %s");

tstring ctxInfo = _T(" ");
if (v.tid != ctorThread) {
ctxInfo = (tstrings::any() << " (TID: " << v.tid << ") ").tstr();
}

const tstring buf = tstrings::unsafe_format(format,
unsigned(v.ts.wHour), unsigned(v.ts.wMinute), unsigned(v.ts.wSecond), unsigned(v.ts.wMilliseconds), // time
ctxInfo.c_str(),
v.fileName.c_str(), v.lineNum, v.funcName.c_str(),
v.logLevel.c_str(),
v.message.c_str());

DatabaseRecord r(1);
r.setString(0, _T("Java [1]"));
r.setString(1, buf);

MsiProcessMessage(handle, INSTALLMESSAGE_INFO, r.getHandle());
}


MsiLogTrigger::MsiLogTrigger(MSIHANDLE h):
msiLogAppender(h),
oldLogAppender(Logger::defaultLogger().getAppender()),
teeLogAppender(&msiLogAppender, &oldLogAppender) {
Logger::defaultLogger().setAppender(teeLogAppender);
}


MsiLogTrigger::~MsiLogTrigger() {
Logger::defaultLogger().setAppender(oldLogAppender);
}




namespace {
MSIHANDLE openDatabase(const CA& ca) {
MSIHANDLE h = MsiGetActiveDatabase(ca.getHandle());
if (h == NULL) {
JP_THROW(Error(std::string("MsiGetActiveDatabase() failed"),
ERROR_FUNCTION_FAILED));
}
return h;
}

} // namespace

Database::Database(const CA& ca): msiPath(_T("*CA*")),
dbHandle(openDatabase(ca)) {
}

} // namespace msi

1 comment on commit 977e094

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

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

Please sign in to comment.