diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index fdda3c0346..2c539c4429 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -42,6 +42,9 @@ jobs: sudo apt install libmpfr-dev #required by rmpfr packages sudo apt install libglpk-dev #required by igraph packages sudo apt install jags + sudo apt install libminizip-dev # required by freexl + git clone https://github.com/jasp-stats/freexl.git + cd freexl && ./configure && make && sudo make install - name: Install boost uses: MarkusJx/install-boost@v2.4.4 diff --git a/Common/utilenums.h b/Common/utilenums.h index 510717923f..a51fecf8c5 100644 --- a/Common/utilenums.h +++ b/Common/utilenums.h @@ -2,7 +2,7 @@ #define UTILENUMS_H #include "enumutilities.h" -DECLARE_ENUM(FileTypeBase, jasp = 0, html, csv, txt, tsv, sav, zsav, ods, pdf, sas7bdat, sas7bcat, por, xpt, dta, database, empty, unknown ); +DECLARE_ENUM(FileTypeBase, jasp = 0, html, csv, txt, tsv, sav, zsav, ods, xls, xlsx, pdf, sas7bdat, sas7bcat, por, xpt, dta, database, empty, unknown ); //const QStringList Database::dbTypes() const should be updated if DbType is changed. DECLARE_ENUM(DbType, NOTCHOSEN, QDB2, /*QIBASE,*/ QMYSQL, QOCI, QODBC, QPSQL, QSQLITE /*, QSQLITE2, QTDS*/ ); diff --git a/Desktop/CMakeLists.txt b/Desktop/CMakeLists.txt index 2cb64324fd..79e8c4e39b 100644 --- a/Desktop/CMakeLists.txt +++ b/Desktop/CMakeLists.txt @@ -42,7 +42,7 @@ qt_add_executable( ${BUNDLE_RESOURCES} $<$:${CMAKE_CURRENT_LIST_DIR}/icon.rc> $<$:${_R_Framework}> - $<$:${CMAKE_SOURCE_DIR}/Desktop/JASP.exe.manifest> + $<$:${CMAKE_SOURCE_DIR}/Desktop/JASP.exe.manifest> ) set( @@ -105,6 +105,7 @@ target_include_directories( $<$:/app/include/QtCore5Compat> $<$:/app/include/QtWebEngineQuick> $<$:/app/include/QtWebEngineCore> + ${LIBFREEXL_INCLUDE_DIRS} ) target_link_libraries( @@ -145,6 +146,9 @@ target_link_libraries( Iconv::Iconv OpenSSL::SSL OpenSSL::Crypto + # FreeXL + ${LIBFREEXL_LIBRARIES} + $<$>:freexl::freexl> # ReadStat ----------------------------------- ${LIBREADSTAT_LIBRARIES} # MinGW's ReadStat diff --git a/Desktop/data/datasetloader.cpp b/Desktop/data/datasetloader.cpp index 115c13a2aa..6f37f87638 100644 --- a/Desktop/data/datasetloader.cpp +++ b/Desktop/data/datasetloader.cpp @@ -25,6 +25,8 @@ #include "importers/jaspimporterold.h" #include "importers/odsimporter.h" #include "importers/readstatimporter.h" +#include "importers/excelimporter.h" + #include @@ -51,6 +53,8 @@ Importer* DataSetLoader::getImporter(const string & locator, const string &ext) boost::iequals(ext,".txt") || boost::iequals(ext,".tsv")) return new CSVImporter(); if( boost::iequals(ext,".ods")) return new ODSImporter(); + if( boost::iequals(ext,".xls") || + boost::iequals(ext,".xlsx")) return new ExcelImporter(); if( ReadStatImporter::extSupported(ext)) return new ReadStatImporter(ext); return nullptr; //If NULL then JASP will try to load it as a .jasp file (if the extension matches) diff --git a/Desktop/data/fileevent.cpp b/Desktop/data/fileevent.cpp index 1922f15943..68bc9c3c46 100644 --- a/Desktop/data/fileevent.cpp +++ b/Desktop/data/fileevent.cpp @@ -129,7 +129,9 @@ QString FileEvent::getProgressMsg() const case Utils::FileType::txt: case Utils::FileType::tsv: case Utils::FileType::ods: return tr("Importing Data from %1").arg(FileTypeBaseToQString(_type).toUpper()); - case Utils::FileType::sav: + case Utils::FileType::xls: + case Utils::FileType::xlsx: return tr("Importing Excel File"); + case Utils::FileType::sav: case Utils::FileType::zsav: case Utils::FileType::por: return tr("Importing SPSS File"); case Utils::FileType::xpt: diff --git a/Desktop/data/importers/csvimporter.cpp b/Desktop/data/importers/csvimporter.cpp index 25875bfac1..165a27110f 100644 --- a/Desktop/data/importers/csvimporter.cpp +++ b/Desktop/data/importers/csvimporter.cpp @@ -34,7 +34,7 @@ ImportDataSet* CSVImporter::loadFile(const string &locator, std::function importColumns; diff --git a/Desktop/data/importers/excel/excel.cpp b/Desktop/data/importers/excel/excel.cpp new file mode 100644 index 0000000000..a5f1657194 --- /dev/null +++ b/Desktop/data/importers/excel/excel.cpp @@ -0,0 +1,119 @@ +// +// Copyright (C) 2013-2024 University of Amsterdam +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "excel.h" +#include "utilities/qutils.h" +#include + +#include +#include + +Excel::Excel(const std::string &locator) +{ + _path = locator; +} + +void Excel::open() +{ + QFileInfo fi(tq(_path)); + _fileSize = fi.size(); + + if (_fileSize < 0) + throw std::runtime_error("Could not access file"); + + if (_fileSize == 0) + throw std::runtime_error("File is empty"); +} + +void Excel::openWorkbook() +{ + QString xlsFilePath = tq(_path); + const char* utf8Path = _path.c_str(); //But it would be better to just use _path.c_str() directly if you need it. It is in utf8 in any case. + QString extension = QFileInfo(xlsFilePath).suffix().toLower(); + + int ret = 0; + if (extension == "xls") + ret = freexl_open(utf8Path, &_handle); + else if (extension == "xlsx") + ret = freexl_open_xlsx(utf8Path, &_handle); + else + throw std::runtime_error("Unsupported file format: " + fq(extension)); + + if(ret != FREEXL_OK) + throw std::runtime_error("Unexpected error while loading excel file, error code: " + std::to_string(ret)); +} + +void Excel::selectActiveWorksheet() +{ + int ret = freexl_select_active_worksheet(_handle, 0); // import the first worksheet(index=0) by default. + if (ret != FREEXL_OK) + throw std::runtime_error("Could not select active worksheet,\n error code: " + std::to_string(ret)); +} + +void Excel::getWorksheetDimensions(uint32_t &rows, uint16_t &cols) { + int ret = freexl_worksheet_dimensions(_handle, &rows, &cols); + + if (ret != FREEXL_OK) + throw std::runtime_error("Could not read worksheet dimensions, error code: " + std::to_string(ret)); + + _numCols = cols; //get cols count while read sheet +} + +void Excel::getCellValue(uint32_t &row, uint16_t &col, std::string &cellValue) +{ + FreeXL_CellValue cell; + int ret = freexl_get_cell_value(_handle, row, col, &cell); + + if (ret != FREEXL_OK) + cellValue = "ERROR " + std::to_string(ret); + + switch (cell.type) + { + case FREEXL_CELL_TEXT: + case FREEXL_CELL_SST_TEXT: + case FREEXL_CELL_DATE: // So we store it as a character for now until support for date types. + case FREEXL_CELL_DATETIME: + case FREEXL_CELL_TIME: + cellValue = cell.value.text_value; + cellValue = stringUtils::replaceBy(cellValue, "\n", "_"); + break; + case FREEXL_CELL_INT: + cellValue = std::to_string(cell.value.int_value); + break; + case FREEXL_CELL_DOUBLE: + cellValue = std::to_string(cell.value.double_value); + break; + case FREEXL_CELL_NULL: + default: + cellValue = ""; + break; + } +} + +uint16_t Excel::countCols() +{ + return _numCols; +} + +void Excel::close() +{ + if (_handle) + { + freexl_close(_handle); + _handle = nullptr; + } +} diff --git a/Desktop/data/importers/excel/excel.h b/Desktop/data/importers/excel/excel.h new file mode 100644 index 0000000000..9eebfb16e3 --- /dev/null +++ b/Desktop/data/importers/excel/excel.h @@ -0,0 +1,53 @@ +// +// Copyright (C) 2013-2024 University of Amsterdam +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef EXCEL_H +#define EXCEL_H + +#include +#include + +#include + +class Excel +{ +public: + Excel(const std::string &path); + + void open(); + void close(); + + void openWorkbook(); + void selectActiveWorksheet(); + void getWorksheetDimensions(uint32_t &rows, uint16_t &cols); + void getCellValue(uint32_t &row, uint16_t &col, std::string &cellValue); + + uint16_t countCols(); + +private: + + long _fileSize; + long _filePosition; + uint16_t _numCols; + +private: + + std::string _path; + const void *_handle; +}; + +#endif // EXCEL_H diff --git a/Desktop/data/importers/excel/excelimportcolumn.cpp b/Desktop/data/importers/excel/excelimportcolumn.cpp new file mode 100644 index 0000000000..a9cf48a2f8 --- /dev/null +++ b/Desktop/data/importers/excel/excelimportcolumn.cpp @@ -0,0 +1,31 @@ +#include "excelimportcolumn.h" +#include "timers.h" + +ExcelImportColumn::ExcelImportColumn(ImportDataSet* importDataSet, std::string name) : ImportColumn(importDataSet, name) +{ +} + +ExcelImportColumn::ExcelImportColumn(ImportDataSet *importDataSet, std::string name, long reserve) : ImportColumn(importDataSet, name) +{ + _data.reserve(reserve); +} + +ExcelImportColumn::~ExcelImportColumn() +{ + JASPTIMER_SCOPE(ExcelImportColumn::~ExcelImportColumn()); +} + +size_t ExcelImportColumn::size() const +{ + return _data.size(); +} + +void ExcelImportColumn::addValue(const std::string &value) +{ + _data.push_back(value); +} + +const std::vector &ExcelImportColumn::getValues() const +{ + return _data; +} diff --git a/Desktop/data/importers/excel/excelimportcolumn.h b/Desktop/data/importers/excel/excelimportcolumn.h new file mode 100644 index 0000000000..c84a563c09 --- /dev/null +++ b/Desktop/data/importers/excel/excelimportcolumn.h @@ -0,0 +1,26 @@ +#ifndef EXCELIMPORTCOLUMN_H +#define EXCELIMPORTCOLUMN_H + +#include "data/importers/importcolumn.h" + + +class ExcelImportColumn : public ImportColumn +{ +public: + ExcelImportColumn(ImportDataSet* importDataSet, std::string name); + ExcelImportColumn(ImportDataSet* importDataSet, std::string name, long reserve); + ~ExcelImportColumn() override; + + size_t size() const override; + const stringvec & allValuesAsStrings() const override { return _data; } + void addValue(const std::string &value); + const stringvec & getValues() const; + + +private: + stringvec _data; + +}; + + +#endif // EXCELIMPORTCOLUMN_H diff --git a/Desktop/data/importers/excelimporter.cpp b/Desktop/data/importers/excelimporter.cpp new file mode 100644 index 0000000000..f159407a0d --- /dev/null +++ b/Desktop/data/importers/excelimporter.cpp @@ -0,0 +1,106 @@ +// +// Copyright (C) 2013-2024 University of Amsterdam +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "excelimporter.h" +#include "data/importers/excel/excel.h" +#include "data/importers/excel/excelimportcolumn.h" +#include +#include +#include +#include + + +ExcelImporter::ExcelImporter() : Importer() {} + +ImportDataSet* ExcelImporter::loadFile(const std::string &locator, std::function progressCallback) +{ + JASPTIMER_RESUME(ExcelImporter::loadFile); + + ImportDataSet* data = new ImportDataSet(this); + stringvec colNames; + + uint16_t cols; + uint32_t rows; + uint32_t row; + uint16_t col; + + std::vector importColumns; + + Excel excel(locator); + excel.open(); + progressCallback(3); + + excel.openWorkbook(); + progressCallback(5); + + excel.selectActiveWorksheet(); + progressCallback(10); + + excel.getWorksheetDimensions(rows, cols); + progressCallback(25); + + cols = excel.countCols(); + importColumns.reserve(cols); + + for (uint32_t row = 0; row < rows; ++row) + { + stringvec lineValues; + + for (uint16_t col = 0; col < cols; ++col) + { + std::string cellValue; + excel.getCellValue(row, col, cellValue); + lineValues.push_back(cellValue); + } + + if (row == 0) + { + colNames = lineValues; + importColumns.reserve(colNames.size()); + + for (int i = 0; i < colNames.size(); ++i) + { + std::string colName = colNames[i]; + if (colName.empty()) + colName = "V" + std::to_string(i + 1); + else if(ColumnUtils::isIntValue(colName) || ColumnUtils::isDoubleValue(colName)) + colName = "V" + colName; + // distinguish duplicate column names + if(std::find(colNames.begin(), colNames.begin() + i, colName) != colNames.begin() + i) + colName += "_" + std::to_string(i + 1); + + colNames[i] = colName; + importColumns.push_back(new ExcelImportColumn(data, colName, rows - 1)); + } + } + else + { + for (int i = 0; i < importColumns.size(); ++i) + importColumns[i]->addValue(i < lineValues.size() ? lineValues[i] : ""); + } + } + + for (ExcelImportColumn* col : importColumns) + data->addColumn(col); + + data->buildDictionary(); + excel.close(); + + JASPTIMER_STOP(ExcelImporter::loadFile); + + return data; +} diff --git a/Desktop/data/importers/excelimporter.h b/Desktop/data/importers/excelimporter.h new file mode 100644 index 0000000000..52b0c31dfa --- /dev/null +++ b/Desktop/data/importers/excelimporter.h @@ -0,0 +1,40 @@ +// +// Copyright (C) 2013-2024 University of Amsterdam +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef EXCELIMPORTER_H +#define EXCELIMPORTER_H + +#include "importer.h" +#include +#include "timers.h" + + +class ExcelImporter : public Importer +{ + Q_DECLARE_TR_FUNCTIONS(ExcelImporter) +public: + ExcelImporter(); + virtual ~ExcelImporter() {} + +protected: + ImportDataSet* loadFile(const std::string &locator, std::function progressCallback) override; + +private: + JASPTIMER_CLASS(ExcelImporter); +}; + +#endif // EXCELIMPORTER_H diff --git a/Desktop/mainwindow.cpp b/Desktop/mainwindow.cpp index 8df39b3c65..f0612cf20f 100644 --- a/Desktop/mainwindow.cpp +++ b/Desktop/mainwindow.cpp @@ -1737,7 +1737,7 @@ bool MainWindow::startDataEditorHandler() else { QString caption = "Find Data File"; - QString filter = "Data File (*.csv *.txt *.tsv *.sav *.ods)"; + QString filter = "Data File (*.csv *.txt *.tsv *.sav *.ods *.xls *.xlsx)"; dataFilePath = MessageForwarder::browseOpenFile(caption, "", filter); if (dataFilePath == "") diff --git a/Desktop/widgets/filemenu/computer.cpp b/Desktop/widgets/filemenu/computer.cpp index 247c92b4ec..58314facb6 100644 --- a/Desktop/widgets/filemenu/computer.cpp +++ b/Desktop/widgets/filemenu/computer.cpp @@ -39,9 +39,16 @@ FileEvent *Computer::browseOpen(const QString &path) else browsePath = path; - QString filter = "Data Sets (*.jasp *.csv *.txt *.tsv *.sav *.zsav *.ods *.dta *.por *.sas7bdat *.sas7bcat *.xpt)"; + QString filter = tr("All Data Sets %1").arg("(*.jasp *.csv *.txt *.tsv *.sav *.zsav *.ods *.xls *.xlsx *.dta *.por *.sas7bdat *.sas7bcat *.xpt);;") + + tr("JASP Files %1").arg("(*.jasp);;") + + tr("CSV Text Files %1").arg("(*.csv *.txt *.tsv);;") + + tr("Spreadsheet Files %1").arg("(*.ods *.xls *.xlsx);;") + + tr("SPSS Files %1").arg("(*.sav *.zsav *.por)") + ";;" + + tr("Stata Files %1").arg("(*.dta);;") + + tr("SAS Files %1").arg("(*.sas7bdat *.sas7bcat *.xpt)"); + if (mode() == FileEvent::FileSyncData) - filter = "Data Sets (*.csv *.txt *.tsv *.sav *.ods)"; + filter = "Data Sets (*.csv *.txt *.tsv *.sav *.ods *.xls *.xlsx)"; Log::log() << "Now calling MessageForwarder::browseOpenFile(\"Open\", \"" << browsePath.toStdString() << "\", \"" << filter.toStdString() << "\")" << std::endl; QString finalPath = MessageForwarder::browseOpenFile("Open", browsePath, filter); @@ -89,7 +96,7 @@ FileEvent *Computer::browseSave(const QString &path, FileEvent::FileMode mode) case FileEvent::FileSyncData: caption = tr("Sync Data"); - filter = tr("Data Files") += " (*.csv *.txt *.tsv *.sav *.ods)"; + filter = tr("Data Files") += " (*.csv *.txt *.tsv *.sav *.ods *.xls *.xlsx)"; browsePath += ".csv"; break; diff --git a/Desktop/widgets/filemenu/filemenu.cpp b/Desktop/widgets/filemenu/filemenu.cpp index c044de713d..a2e5b8f4e6 100644 --- a/Desktop/widgets/filemenu/filemenu.cpp +++ b/Desktop/widgets/filemenu/filemenu.cpp @@ -172,7 +172,7 @@ void FileMenu::sync() tr("JASP has no associated data file to be synchronized with.\nDo you want to search for such a data file on your computer?\nNB: You can also set this data file via menu File/Sync Data."))) return; - path = MessageForwarder::browseOpenFile(tr("Find Data File"), "", tr("Data File").arg("*.csv *.txt *.tsv *.sav *.zsav *.ods *.dta *.por *.sas7bdat *.sas7bcat *.xpt")); + path = MessageForwarder::browseOpenFile(tr("Find Data File"), "", tr("Data File").arg("*.csv *.txt *.tsv *.sav *.zsav *.ods *.xls *.xlsx *.dta *.por *.sas7bdat *.sas7bcat *.xpt")); } _mainWindow->setCheckAutomaticSync(false); diff --git a/Docs/development/jasp-build-guide-linux.md b/Docs/development/jasp-build-guide-linux.md index ad3576cca1..b4bbbc1a6a 100644 --- a/Docs/development/jasp-build-guide-linux.md +++ b/Docs/development/jasp-build-guide-linux.md @@ -23,6 +23,8 @@ If you have not cloned the `jasp-desktop` repository, please head back to the [b - `sqlite`^1 - `V8` (for `jaspProcess`) - `zlib`^1 + - `freexl`^1 + - Qt (>= 6.7) - Qt Creator 13 @@ -38,7 +40,7 @@ Based on your system, you can install the mentioned libraries using your package On Ubuntu, you can use `apt`. ``` -sudo apt install libboost-dev libjsoncpp25 libjsoncpp-dev libarchive13 libarchive-dev libxcb-xkb-dev libxcb-xkb1 libxcb-xinerama0 libxcb-cursor0 libxkbcommon-dev libxkbcommon-x11-dev autoconf zlib1g zlib1g-dev cmake gfortran build-essential flex libssl-dev libgl1-mesa-dev libsqlite3-dev r-base libglpk-dev +sudo apt install libboost-dev libjsoncpp25 libjsoncpp-dev libarchive13 libarchive-dev libxcb-xkb-dev libxcb-xkb1 libxcb-xinerama0 libxcb-cursor0 libxkbcommon-dev libxkbcommon-x11-dev autoconf zlib1g zlib1g-dev cmake gfortran build-essential flex libssl-dev libgl1-mesa-dev libsqlite3-dev r-base libglpk-dev libminizip-dev libfreexl-dev ``` > ⚠️ Some of these libraries might not be up-to-date and as a result JASP will complain. If this happens, you need to download, make and install those libraries individually. Alternatively, you can use the [Linux version of Homebrew](https://docs.brew.sh/Homebrew-on-Linux) and install the up-to-dated libraries locally. @@ -86,9 +88,9 @@ You also need Qt Creator and Qt 6 to be able to build and test JASP's libraries - [x] CMake - [x] Ninja -### ReadStat and JAGS +### Dependencies -On Linux JASP's CMake script will download and install ReadStat and JAGS for you when necessary. +On Linux JASP's CMake script will download and install ReadStat, JAGS and FreeXL for you when necessary. ### Installing Qt Creator / Qt diff --git a/Engine/CMakeLists.txt b/Engine/CMakeLists.txt index bfa5030a85..d2f87aac6d 100644 --- a/Engine/CMakeLists.txt +++ b/Engine/CMakeLists.txt @@ -107,16 +107,14 @@ if(WINDOWS) POST_BUILD DEPENDS ${CMAKE_BINARY_DIR}/R-Interface/libR-Interface.dll COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_LIBGCC_S_SEH_DLL} ${CMAKE_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_LIBSTDCPP_DLL} - ${CMAKE_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_MSYS_DLL} - ${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_LIBSTDCPP_DLL} + ${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_MSYS_DLL} + ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_LIBWINPTHREAD_DLL} ${CMAKE_BINARY_DIR} - #COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_LIBJSONCPP_DLL} - # ${CMAKE_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_LIBREADSTAT_DLL} - ${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_LIBREADSTAT_DLL} + ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_ZLIB_DLL} ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTOOLS_LIBICONV_DLL} diff --git a/Modules/pkgdepends.lock b/Modules/pkgdepends.lock index 15682ea82e..abe373f5f8 100644 --- a/Modules/pkgdepends.lock +++ b/Modules/pkgdepends.lock @@ -1,6 +1,6 @@ { "R": { - "Version": "4.3.3", + "Version": "4.4.1", "Repositories": [ { "Name": "CRAN", @@ -57,7 +57,7 @@ }, "curl": { "Package": "curl", - "Version": "5.0.2", + "Version": "5.2.1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -99,7 +99,7 @@ }, "jsonlite": { "Package": "jsonlite", - "Version": "1.8.7", + "Version": "1.8.8", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -109,7 +109,7 @@ }, "lpSolve": { "Package": "lpSolve", - "Version": "5.6.18", + "Version": "5.6.20", "Source": "Repository", "Repository": "CRAN", "Hash": "12c7a918599d5700e4265dd8a21f684f" diff --git a/Tools/CMake/Install.cmake b/Tools/CMake/Install.cmake index be07380e7c..76e92fa427 100644 --- a/Tools/CMake/Install.cmake +++ b/Tools/CMake/Install.cmake @@ -384,10 +384,10 @@ if(WIN32) install( FILES ${RTOOLS_LIBGCC_S_SEH_DLL} ${RTOOLS_LIBSTDCPP_DLL} - ${RTOOLS_MSYS_DLL} + ${RTOOLS_MSYS_DLL} ${RTOOLS_LIBWINPTHREAD_DLL} - #${RTOOLS_LIBJSONCPP_DLL} - ${RTOOLS_LIBREADSTAT_DLL} + #${RTOOLS_LIBJSONCPP_DLL} + ${RTOOLS_LIBREADSTAT_DLL} ${RTOOLS_ZLIB_DLL} ${RTOOLS_LIBICONV_DLL} ${_LIB_R_INTERFACE_DLL} diff --git a/Tools/CMake/Libraries.cmake b/Tools/CMake/Libraries.cmake index cda01d5524..d61756db74 100644 --- a/Tools/CMake/Libraries.cmake +++ b/Tools/CMake/Libraries.cmake @@ -199,6 +199,26 @@ if(LINUX) ) endif() + # ---- FreeXL ---- + message(CHECK_START "Looking for `libfreexl`") + set(LIBFREEXL_INCLUDE_DIRS /usr/include) + set(LIBFREEXL_LIBRARY_DIRS /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu /usr/lib/aarch64-linux-gnu) + + message(CHECK_START "Looking for libfreexl.so") + find_library(LIBFREEXL_LIBRARIES libfreexl.so + HINTS ${LIBFREEXL_LIBRARY_DIRS} REQUIRED) + + if(EXISTS ${LIBFREEXL_LIBRARIES}) + message(CHECK_PASS "found") + message(STATUS " ${LIBFREEXL_LIBRARIES}") + else() + message(CHECK_FAIL "not found") + message( + FATAL_ERROR + "FreeXL is required for building on Linux, please follow the build instruction before you continue." + ) + endif() + find_package(PkgConfig) #pkg_check_modules(_PKGCONFIG_LIB_JSONCPP REQUIRED jsoncpp>=1.9) @@ -209,10 +229,14 @@ if(APPLE) message(CHECK_START "Looking for 'libbrotlicommon'") find_package(Brotli 1.0.9 REQUIRED) + find_package(freexl 2.0.0 REQUIRED) endif() if(WIN32) + + find_package(freexl 2.0.0 REQUIRED) + # ReadStat message(CHECK_START "Looking for libreadstat.dll.a") @@ -269,7 +293,6 @@ if(WIN32) ) endif() - message(CHECK_START "Looking for zlib1.dll") find_file( RTOOLS_ZLIB_DLL @@ -380,103 +403,6 @@ if(WIN32) ) endif() - #message(CHECK_START "Looking for libjsoncpp-24.dll") - #find_file( - # RTOOLS_LIBJSONCPP_DLL - # NAMES libjsoncpp-24.dll - # PATHS ${RTOOLS_PATH}/bin - # NO_DEFAULT_PATH) - - #if(EXISTS ${RTOOLS_LIBJSONCPP_DLL}) - # message(CHECK_PASS "found") - # message(STATUS " ${RTOOLS_LIBJSONCPP_DLL}") - #else() - # message(CHECK_FAIL "not found") - # message( - # FATAL_ERROR - # "MSYS2 and some of its libraries are required for building on Windows, please follow the build instruction before you continue." - # ) - #endif() - - # jags - # This could all go into its module later, and these can - # turn into a function, but I don't want to do it now - # because I'm uncertain about CMake variable scopping - # message(CHECK_START "Looking for jags files") - # find_file( - # RTOOLS_LIBJAGS_BAT - # NAMES jags.bat - # PATHS ${RTOOLS_PATH}/bin REQUIRED) - # message(STATUS " ${RTOOLS_LIBJAGS_BAT}") - # find_file( - # RTOOLS_LIBJAGS - # NAMES libjags-4.dll - # PATHS ${RTOOLS_PATH}/bin REQUIRED) - # message(STATUS " ${RTOOLS_LIBJAGS}") - # find_file( - # RTOOLS_LIBJAGS_JRMATH - # NAMES libjrmath-0.dll - # PATHS ${RTOOLS_PATH}/bin REQUIRED) - # message(STATUS " ${RTOOLS_LIBJAGS_JRMATH}") - # find_file( - # RTOOLS_LIB_BLAS - # NAMES libblas.dll - # PATHS ${RTOOLS_PATH}/bin REQUIRED) - # message(STATUS " ${RTOOLS_LIB_BLAS}") - # find_file( - # RTOOLS_LIB_LAPACK - # NAMES liblapack.dll - # PATHS ${RTOOLS_PATH}/bin REQUIRED) - # message(STATUS " ${RTOOLS_LIB_LAPACK}") - - # set(RTOOLS_LIBJAGS_HEADERS_PATH "${RTOOLS_PATH}/include/JAGS") - # message(STATUS " ${RTOOLS_LIBJAGS_HEADERS_PATH}") - # set(RTOOLS_LIBJAGS_LIBRARIES_PATH "${RTOOLS_PATH}/lib/JAGS") - # message(STATUS " ${RTOOLS_LIBJAGS_LIBRARIES_PATH}") - # set(RTOOLS_LIBJAGS_PKGCONFIG_PATH "${RTOOLS_PATH}/lib/pkgconfig") - # message(STATUS " ${RTOOLS_LIBJAGS_PKGCONFIG_PATH}") - # set(RTOOLS_LIBJAGS_MODULES_PATH "${RTOOLS_PATH}/lib/JAGS/modules-4") - # message(STATUS " ${RTOOLS_LIBJAGS_MODULES_PATH}") - - # find_file( - # RTOOLS_LIBJAGS_LIBJAGS_A - # NAMES libjags.dll.a - # PATHS ${RTOOLS_PATH}/lib REQUIRED) - # message(STATUS " ${RTOOLS_LIBJAGS_LIBJAGS_A}") - # find_file( - # RTOOLS_LIBJAGS_LIBJAGS_LA - # NAMES libjags.la - # PATHS ${RTOOLS_PATH}/lib REQUIRED) - # message(STATUS " ${RTOOLS_LIBJAGS_LIBJAGS_LA}") - # find_file( - # RTOOLS_LIBJAGS_LIBJRMATH_A - # NAMES libjrmath.dll.a - # PATHS ${RTOOLS_PATH}/lib REQUIRED) - # message(STATUS " ${RTOOLS_LIBJAGS_LIBJRMATH_A}") - # find_file( - # RTOOLS_LIBJAGS_LIBJRMATH_LA - # NAMES libjrmath.la - # PATHS ${RTOOLS_PATH}/lib REQUIRED) - # message(STATUS " ${RTOOLS_LIBJAGS_LIBJRMATH_LA}") - # find_file( - # RTOOLS_LIB_BLAS_DLL_A - # NAMES libblas.dll.a - # PATHS ${RTOOLS_PATH}/lib REQUIRED) - # message(STATUS " ${RTOOLS_LIB_BLAS_DLL_A}") - # find_file( - # RTOOLS_LIB_LAPACK_DLL_A - # NAMES liblapack.dll.a - # PATHS ${RTOOLS_PATH}/lib REQUIRED) - # message(STATUS " ${RTOOLS_LIB_LAPACK_DLL_A}") - - # find_file( - # RTOOLS_LIBJAGS_JAGS_TERMINAL_EXE - # NAMES jags-terminal.exe - # PATHS ${RTOOLS_PATH}/libexec REQUIRED) - # message(STATUS " ${RTOOLS_LIBJAGS_JAGS_TERMINAL_EXE}") - - # message(CHECK_PASS "found") - endif() list(POP_BACK CMAKE_MESSAGE_CONTEXT) diff --git a/Tools/macOS/Info.plist.in b/Tools/macOS/Info.plist.in index a5423ca3ea..04ce379b33 100644 --- a/Tools/macOS/Info.plist.in +++ b/Tools/macOS/Info.plist.in @@ -84,10 +84,17 @@ CFBundleTypeExtensions csv + txt tsv sav + zsav ods - txt + xls + xlsx + sas7bdat + sas7bcat + por + dta diff --git a/Tools/windows/msix/AppxManifest-nightly.xml.in b/Tools/windows/msix/AppxManifest-nightly.xml.in index c43481c078..c06d60d3bd 100644 --- a/Tools/windows/msix/AppxManifest-nightly.xml.in +++ b/Tools/windows/msix/AppxManifest-nightly.xml.in @@ -55,6 +55,8 @@ .csv .sav .ods + .xls + .xlsx .zsav .xpt .por diff --git a/Tools/windows/msix/AppxManifest-sideload.xml.in b/Tools/windows/msix/AppxManifest-sideload.xml.in index 56dbe8ca11..ba08066439 100644 --- a/Tools/windows/msix/AppxManifest-sideload.xml.in +++ b/Tools/windows/msix/AppxManifest-sideload.xml.in @@ -55,6 +55,8 @@ .csv .sav .ods + .xls + .xlsx .zsav .xpt .por diff --git a/Tools/windows/msix/AppxManifest-store-beta.xml.in b/Tools/windows/msix/AppxManifest-store-beta.xml.in index ab33bc8fd0..fae2b0ade1 100644 --- a/Tools/windows/msix/AppxManifest-store-beta.xml.in +++ b/Tools/windows/msix/AppxManifest-store-beta.xml.in @@ -55,6 +55,8 @@ .csv .sav .ods + .xls + .xlsx .zsav .xpt .por diff --git a/Tools/windows/msix/AppxManifest-store.xml.in b/Tools/windows/msix/AppxManifest-store.xml.in index 84eebdd1c5..82b7a389f6 100644 --- a/Tools/windows/msix/AppxManifest-store.xml.in +++ b/Tools/windows/msix/AppxManifest-store.xml.in @@ -55,6 +55,8 @@ .csv .sav .ods + .xls + .xlsx .zsav .xpt .por diff --git a/conanfile.txt b/conanfile.txt index f8f27a7ac8..d99bc15de4 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -18,6 +18,7 @@ brotli/1.0.9 sqlite3/3.46.0 gmp/6.3.0 mpfr/4.2.1 +freexl/2.0.0 [generators] CMakeDeps