diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..09fac91
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/tyxml-*.tbz
diff --git a/ocaml-tyxml.rpmlintrc b/ocaml-tyxml.rpmlintrc
new file mode 100644
index 0000000..a287b98
--- /dev/null
+++ b/ocaml-tyxml.rpmlintrc
@@ -0,0 +1,8 @@
+# THIS FILE IS FOR WHITELISTING RPMLINT ERRORS AND WARNINGS IN TASKOTRON
+# https://fedoraproject.org/wiki/Taskotron/Tasks/dist.rpmlint#Whitelisting_errors
+
+# The dictionary is missing some technical terms
+addFilter(r'W: spelling-error .* (combinators|dom|eliom|href|html|js)')
+
+# Documentation is in the doc subpackage
+addFilter(r'ocaml-tyxml[^:]+: W: no-documentation')
diff --git a/ocaml-tyxml.spec b/ocaml-tyxml.spec
new file mode 100644
index 0000000..2016faa
--- /dev/null
+++ b/ocaml-tyxml.spec
@@ -0,0 +1,211 @@
+%ifnarch %{ocaml_native_compiler}
+%global debug_package %{nil}
+%endif
+
+%global srcname tyxml
+
+Name: ocaml-%{srcname}
+Version: 4.3.0
+Release: 1%{?dist}
+Summary: Build valid HTML and SVG documents
+
+License: LGPLv2 with exceptions
+URL: https://ocsigen.org/tyxml/
+Source0: https://github.com/ocsigen/tyxml/releases/download/%{version}/%{srcname}-%{version}.tbz
+
+BuildRequires: ocaml >= 4.02
+BuildRequires: ocaml-alcotest-devel
+BuildRequires: ocaml-astring-devel
+BuildRequires: ocaml-dune
+BuildRequires: ocaml-findlib
+BuildRequires: ocaml-markup-devel >= 0.7.2
+BuildRequires: ocaml-migrate-parsetree-devel
+BuildRequires: ocaml-ocamldoc
+BuildRequires: ocaml-ppx-derivers-devel
+BuildRequires: ocaml-ppx-tools-versioned-devel
+BuildRequires: ocaml-re-devel
+BuildRequires: ocaml-seq-devel
+BuildRequires: ocaml-uuidm-devel
+BuildRequires: ocaml-uutf-devel
+
+%description
+TyXML provides a set of convenient combinators that uses the OCaml type
+system to ensure the validity of the generated documents. TyXML can be
+used with any representation of HTML and SVG: the textual one, provided
+directly by this package, or DOM trees (`js_of_ocaml-tyxml`), virtual DOM
+(`virtual-dom`) and reactive or replicated trees (`eliom`). You can also
+create your own representation and use it to instantiate a new set of
+combinators.
+
+%package devel
+Summary: Development files for %{name}
+Requires: %{name}%{?_isa} = %{version}-%{release}
+Requires: ocaml-seq-devel%{?_isa}
+
+%description devel
+The %{name}-devel package contains libraries and signature files for
+developing applications that use %{name}.
+
+%package doc
+Summary: HTML documentation for %{name}
+BuildArch: noarch
+
+%description doc
+HTML documentation for %{name}.
+
+%package ppx
+Summary: PPX for writing TyXML documents with HTML syntax
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%description ppx
+This package contains PPX for writing TyXML documents with HTML syntax.
+
+ open Tyxml
+ let%%html to_ocaml = "OCaml!"
+
+The TyXML PPX is compatible with all TyXML instance, from textual trees
+to reactive virtual DOM trees.
+
+%package ppx-devel
+Summary: Development files for %{name}-ppx
+Requires: %{name}-devel%{?_isa} = %{version}-%{release}
+Requires: %{name}-ppx%{?_isa} = %{version}-%{release}
+Requires: ocaml-markup-devel%{?_isa}
+Requires: ocaml-migrate-parsetree-devel%{?_isa}
+
+%description ppx-devel
+The %{name}-ppx-devel package contains libraries and signature files for
+developing applications that use %{name}-ppx.
+
+%prep
+%autosetup -n %{srcname}-%{version}
+
+# Fix typo in 4.3.0; fixed upstream, so remove this when updating
+sed -i 's/onmousdown/onmousedown/' lib/svg_f.ml
+
+# Fix deprecation warning treated as an error by dune.
+# Fixed upstream after the 4.3.0 release; remove this when updating
+sed -i 's/Re\.get/Re.Group.get/' ppx/tyxml_ppx.ml
+
+%build
+dune build
+
+# Build the documentation.
+mkdir html
+ocamldoc -html -d html \
+ -colorize-code -short-functors -charset utf-8 -intro docs/indexdoc \
+ -I _build/install/default/lib/tyxml \
+ -I _build/install/default/lib/tyxml/functor \
+ -I _build/install/default/lib/tyxml/tools \
+ _build/install/default/lib/tyxml/*.mli \
+ _build/install/default/lib/tyxml/functor/*.mli \
+ _build/install/default/lib/tyxml/tools/*.mli
+
+%install
+dune install --destdir=%{buildroot}
+
+# We install the documentation with the doc macro
+rm -fr %{buildroot}%{_prefix}/doc
+
+%ifarch %{ocaml_native_compiler}
+# Add missing executable bits
+find %{buildroot}%{_libdir}/ocaml -name \*.cmxs -exec chmod 0755 {} \+
+%endif
+
+%check
+dune runtest
+
+%files
+%doc CHANGES.md README.md
+%license LICENSE
+%dir %{_libdir}/ocaml/%{srcname}/
+%dir %{_libdir}/ocaml/%{srcname}/functor/
+%dir %{_libdir}/ocaml/%{srcname}/tools/
+%dir %{_libdir}/ocaml/%{srcname}/top/
+%{_libdir}/ocaml/%{srcname}/META
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.cma
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.cmi
+%{_libdir}/ocaml/%{srcname}/functor/*.cma
+%{_libdir}/ocaml/%{srcname}/functor/*.cmi
+%{_libdir}/ocaml/%{srcname}/tools/*.cma
+%{_libdir}/ocaml/%{srcname}/tools/*.cmi
+%{_libdir}/ocaml/%{srcname}/top/*.cma
+%{_libdir}/ocaml/%{srcname}/top/*.cmi
+%ifarch %{ocaml_native_compiler}
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.cmxs
+%{_libdir}/ocaml/%{srcname}/functor/*.cmxs
+%{_libdir}/ocaml/%{srcname}/tools/*.cmxs
+%{_libdir}/ocaml/%{srcname}/top/*.cmxs
+%endif
+
+%files devel
+%{_libdir}/ocaml/%{srcname}/dune-package
+%{_libdir}/ocaml/%{srcname}/opam
+%ifarch %{ocaml_native_compiler}
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.a
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.cmx
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.cmxa
+%{_libdir}/ocaml/%{srcname}/functor/*.a
+%{_libdir}/ocaml/%{srcname}/functor/*.cmx
+%{_libdir}/ocaml/%{srcname}/functor/*.cmxa
+%{_libdir}/ocaml/%{srcname}/tools/*.a
+%{_libdir}/ocaml/%{srcname}/tools/*.cmx
+%{_libdir}/ocaml/%{srcname}/tools/*.cmxa
+%{_libdir}/ocaml/%{srcname}/top/*.a
+%{_libdir}/ocaml/%{srcname}/top/*.cmx
+%{_libdir}/ocaml/%{srcname}/top/*.cmxa
+%endif
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.cmt
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.cmti
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.ml
+%{_libdir}/ocaml/%{srcname}/%{srcname}*.mli
+%{_libdir}/ocaml/%{srcname}/functor/*.cmt
+%{_libdir}/ocaml/%{srcname}/functor/*.cmti
+%{_libdir}/ocaml/%{srcname}/functor/*.ml
+%{_libdir}/ocaml/%{srcname}/functor/*.mli
+%{_libdir}/ocaml/%{srcname}/tools/*.cmt
+%{_libdir}/ocaml/%{srcname}/tools/*.cmti
+%{_libdir}/ocaml/%{srcname}/tools/*.ml
+%{_libdir}/ocaml/%{srcname}/tools/*.mli
+%{_libdir}/ocaml/%{srcname}/top/*.cmt
+%{_libdir}/ocaml/%{srcname}/top/*.ml
+
+%files ppx
+%dir %{_libdir}/ocaml/%{srcname}-ppx/
+%dir %{_libdir}/ocaml/%{srcname}-ppx/internal/
+%{_libdir}/ocaml/%{srcname}-ppx/META
+%{_libdir}/ocaml/%{srcname}-ppx/ppx.exe
+%{_libdir}/ocaml/%{srcname}-ppx/%{srcname}*.cma
+%{_libdir}/ocaml/%{srcname}-ppx/%{srcname}*.cmi
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.cma
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.cmi
+%ifarch %{ocaml_native_compiler}
+%{_libdir}/ocaml/%{srcname}-ppx/%{srcname}*.cmxs
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.cmxs
+%endif
+
+%files ppx-devel
+%{_libdir}/ocaml/%{srcname}-ppx/dune-package
+%{_libdir}/ocaml/%{srcname}-ppx/opam
+%ifarch %{ocaml_native_compiler}
+%{_libdir}/ocaml/%{srcname}-ppx/%{srcname}*.a
+%{_libdir}/ocaml/%{srcname}-ppx/%{srcname}*.cmx
+%{_libdir}/ocaml/%{srcname}-ppx/%{srcname}*.cmxa
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.a
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.cmx
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.cmxa
+%endif
+%{_libdir}/ocaml/%{srcname}-ppx/%{srcname}*.cmt
+%{_libdir}/ocaml/%{srcname}-ppx/%{srcname}*.ml
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.cmt
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.cmti
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.ml
+%{_libdir}/ocaml/%{srcname}-ppx/internal/*.mli
+
+%files doc
+%doc html/*
+%license LICENSE
+
+%changelog
+* Fri Jan 10 2020 Jerry James - 4.3.0-1
+- Initial RPM
diff --git a/sources b/sources
new file mode 100644
index 0000000..8eb1a13
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+SHA512 (tyxml-4.3.0.tbz) = 4213527061d4db21b6d8e1e159c541a6b3a6f37074526e35052b0dc5c2b1f5b709142d60270247873cbe9160b1b98017f5c28314aabcbee68dc10268970eb821